astrid-runtime/astrid · error

MCP broker did not become ready for principal

Error message

MCP broker did not become ready for principal '{principal}' within {}s; no capsule answered {TOOLS_LIST_TOPIC}

What it means

wait_for_broker_on polls an MCP broker by sending idempotent tools/list requests over the daemon connection until a capsule answers or the ready deadline expires. When the deadline elapses without any capsule responding to TOOLS_LIST_TOPIC, it fails with this error naming the principal and the configured timeout.

Solutions

  1. Increase the readiness timeout (larger ready deadline) and retry.
  2. Verify the principal has capsules registered that serve tools/list (`astrid agent`/capsule listing) and use the correct principal.
  3. Check daemon logs for capsule load failures and restart the daemon/capsules.
  4. Retry after cold start completes — tests show a replayed probe succeeds once the principal's capsules load.
  5. Confirm TOOLS_LIST_TOPIC is supported by the installed capsule versions (version mismatch).

Example fix

// before
astrid mcp ready --principal alice --timeout 5   # capsules still loading
// after
astrid mcp ready --principal alice --timeout 60
Defensive patterns

Strategy: retry

Validate before calling

let deadline = Duration::from_secs(cfg.ready_timeout_secs);
assert!(deadline >= Duration::from_secs(10), "ready timeout too small for capsule cold start");
// also confirm the principal has capsules before probing

Try / catch

match wait_for_broker(&mut io, principal, timeout).await {
    Err(e) if e.to_string().contains("did not become ready") => {
        // extend deadline / verify capsules loaded / retry probe once
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `astrid mcp ready` (via wait_for_broker) or internal readiness probes when no capsule serving the principal's tools/list responds before ready_deadline (as_secs()) elapses.

Common situations: No capsules/agents loaded for that principal at daemon cold start; capsule crashed or is still loading; wrong principal supplied so its capsule set is empty; daemon reachable but broker subsystem wedged; timeout set too short for slow capsule loading.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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

Appendix: source

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

    io: &mut I,
    principal: &PrincipalId,
    ready_deadline: Duration,
    retry_interval: Duration,
) -> Result<()> {
    let deadline = Instant::now()
        .checked_add(ready_deadline)
        .unwrap_or_else(Instant::now);
    let mut outstanding = HashSet::new();

    send_probe(io, principal, &mut outstanding).await?;
    let mut retry_at = Instant::now()
        .checked_add(retry_interval)
        .unwrap_or(deadline);

    loop {
        let now = Instant::now();
        if 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()
            );
        }
        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!(

View on GitHub (pinned to affd8760f4)