astrid-runtime/astrid · error

daemon connection closed while waiting for the MCP broker…

Error message

daemon connection closed while waiting for the MCP broker for principal '{principal}'

What it means

While waiting for the MCP broker, wait_for_broker_on reads frames from the daemon connection with a timeout. If the read returns None (EOF) — the daemon closed the connection before the broker answered — the loop aborts with this error instead of retrying forever on a dead socket.

Solutions

  1. Restart the daemon (`astrid daemon start` or equivalent) and re-run the readiness check.
  2. Retry the readiness command — this is typically transient if the daemon was restarting.
  3. Inspect daemon logs/journal for crash reasons (panic, OOM) and fix the underlying failure.
  4. Ensure nothing concurrently stops the gateway/daemon (e.g. a stray `astrid mcp stop` in scripts) during readiness waits.

Example fix

// before
let frame = match tokio::time::timeout_at(wake_at, io.read_raw_frame()).await {
    Ok(Ok(None)) => bail!("daemon connection closed..."),
    ...
// after (caller-side retry)
for attempt in 0..3 {
    match wait_for_broker(&mut io, principal, deadline).await {
        Ok(()) => break,
        Err(e) if attempt < 2 => { tokio::time::sleep(Duration::from_secs(1)).await; }
        Err(e) => return Err(e),
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// probe the daemon liveness before waiting on the broker
if astrid_core::local_transport::connect_outcome(&daemon_socket).await
    != ConnectOutcome::Connected { eprintln!("daemon not running; start it first"); }

Try / catch

match wait_for_broker(&mut io, principal, timeout).await {
    Err(e) if e.to_string().contains("daemon connection closed") => {
        tokio::time::sleep(Duration::from_secs(1)).await;
        // reconnect to the daemon and retry the readiness wait (bounded retries)
    }
    other => other?,
}

Prevention

When it happens

Trigger: During `astrid mcp ready` / wait_for_broker: the daemon process exits or drops the connection between probes, so read_raw_frame() yields Ok(None) before the deadline.

Common situations: Daemon crash or restart mid-readiness-check; daemon killed by OOM or an admin; connection dropped by an intermediary; connecting to a socket whose daemon just shut down after a stop command.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/f568676771d96d17. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-cli/src/commands/mcp/readiness.rs:119

            anyhow::bail!(
                "MCP broker did not become ready for principal '{principal}' within {}s; no capsule answered {TOOLS_LIST_TOPIC}",
                ready_deadline.as_secs()
            );
        }
        if now >= retry_at {
            debug!(%principal, "MCP broker readiness probe interval elapsed; retrying idempotent tools/list");
            send_probe(io, principal, &mut outstanding).await?;
            retry_at = Instant::now()
                .checked_add(retry_interval)
                .unwrap_or(deadline);
            continue;
        }

        let wake_at = retry_at.min(deadline);
        let frame = match tokio::time::timeout_at(wake_at, io.read_raw_frame()).await {
            Ok(Ok(Some(frame))) => frame,
            Ok(Ok(None)) => {
                anyhow::bail!(
                    "daemon connection closed while waiting for the MCP broker for principal '{principal}'"
                )
            },
            Ok(Err(error)) => {
                return Err(error).with_context(|| {
                    format!(
                        "daemon connection failed while waiting for the MCP broker for principal '{principal}'"
                    )
                });
            },
            Err(_) => {
                if Instant::now() >= deadline {
                    anyhow::bail!(
                        "MCP broker did not become ready for principal '{principal}' within {}s; no capsule answered {TOOLS_LIST_TOPIC}",
                        ready_deadline.as_secs()
                    );
                }
                debug!(%principal, "MCP broker readiness probe unanswered; retrying idempotent tools/list");

View on GitHub (pinned to affd8760f4)