astrid-runtime/astrid · error

MCP broker did not become ready for principal '{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.

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)

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.