Skip to content

Daemon spawn pipe writes empty bytes on mount failure (should write 'error: <reason>') #3

Description

@kpachhai

Summary

When the daemon fails to start due to a mount error (e.g. VaultError: refusing to mount 'X' also as primary), the readiness pipe receives empty bytes instead of the documented error: <reason>\n payload. The MCP client (or any other spawn caller) then raises:

DaemonSpawnError: unexpected readiness payload from spawn pipe: ''

masking the real cause (the underlying VaultError) and forcing the operator to read the daemon log to figure out what actually went wrong.

Protocol

Per src/engram/daemon/spawn.py:140-152, the spawn-pipe protocol is:

text = line.decode("utf-8", errors="replace").rstrip("\n")
if text == "ready":
    return SpawnReadiness.ready()
if text.startswith("error:"):
    return SpawnReadiness.error(text[len("error:"):].strip())
msg = f"unexpected readiness payload from spawn pipe: {text!r}"
raise DaemonSpawnError(msg)

So error: <reason>\n is a first-class outcome. The daemon writes nothing (empty bytes) on certain mount failure paths, which falls into the "unexpected payload" bucket and loses the real error context.

Reproduction

  1. Configure a per-user vault with name: X differing from per-vault vault_name: Y (same path).
  2. Run engram serve (which spawns a daemon).
  3. The daemon fails to mount due to VaultError: refusing to mount 'X' also as primary.
  4. The proxy receives '' on the readiness pipe.
  5. DaemonSpawnError: unexpected readiness payload from spawn pipe: '' propagates to the MCP client.
  6. The real cause (VaultError) is only visible in the daemon log, not surfaced to the caller.

Expected

On any startup-time failure before the daemon would have written ready\n, the daemon should:

write_to_readiness_pipe(f"error: {exception_summary}\n")

so the proxy gets a clean SpawnReadiness.error(...) value with the real reason instead of DaemonSpawnError.

Likely fix locations: the startup paths in src/engram/cli/serve.py and any other place that holds the readiness pipe fd before mount completes. Wrap the mount + ready signal in a try/except that emits error: <msg> on failure.

Context

Surfaced during the 2026-05-16 daemon-fix investigation. The misleading DaemonSpawnError: ... '' was the visible failure; the underlying VaultError (filed as the multi-vault alias issue) was the actual root cause. Surfacing the latter through the readiness pipe would have shortened debugging by ~15 minutes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions