Skip to content

AsyncSessionDB methods silently dropped when called without await (e.g. gateway /model switch) → lost writes + RuntimeWarning #63712

Description

@gmoranxyz

Version

hermes-agent 0.18.0 @ 30e947e · Python gateway running as a systemd user unit

Summary

AsyncSessionDB.__getattr__ (hermes_state.py:5818-5826) returns a coroutine
function for every attribute access on a callable:

def __getattr__(self, name):
    attr = getattr(self._db, name)
    if not callable(attr):
        return attr
    async def _offloaded(*args, **kwargs):
        return await asyncio.to_thread(attr, *args, **kwargs)
    return _offloaded

Any call site that treats an AsyncSessionDB method as synchronous produces a
coroutine that is never awaited: the DB operation never runs and Python emits a
RuntimeWarning. Because it's a generic forwarder, these bugs are invisible to
static analysis — there's no named async def to grep for a missing await.

Observed

On every gateway /model switch:

gateway/slash_commands.py:1773: RuntimeWarning: coroutine
'AsyncSessionDB.__getattr__.<locals>._offloaded' was never awaited

Reproduced on two independent gateways (default + a profile gateway) whenever a
/model command runs. The switch appears to succeed, but a session-DB write
issued during the switch is silently discarded.

Impact

Silent data loss on session state written during the affected paths. Suspected to
be the root cause of the null-system_prompt persistence bug (filed separately),
where the next turn finds no stored system prompt and rebuilds from scratch.

Suggested fixes

  • Make the dropped call site await the method (fix the immediate /model path
    around slash_commands.py:1764-1773 / _finish_switch).
  • Defensively, have AsyncSessionDB guard against sync misuse — e.g. wrap
    _offloaded so a garbage-collected un-awaited coroutine logs the offending
    method name, not just an anonymous _offloaded.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Critical — data loss, security, crash loopcomp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointsweeper:implemented-on-mainSweeper: behavior already present on current mainsweeper:risk-session-stateSweeper risk: may lose/corrupt/mis-associate session or context statetype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions