astrid-runtime/astrid · error · GatewayError::Internal

unexpected response for GetAgentReadiness

Error message

unexpected response for GetAgentReadiness: {other:?}

What it means

The gateway asked the daemon kernel for an agent's readiness status, but the kernel replied with a KernelResponse variant that is neither AgentReadiness nor Error. This is a protocol/programming bug between the gateway and kernel, not a user input problem. The gateway converts it into a GatewayError::Internal with the offending variant debug-printed.

Solutions

  1. Check that gateway and daemon-kernel versions match and rebuild/redeploy both from the same commit
  2. Inspect the printed {other:?} variant to identify which unexpected KernelResponse is being returned
  3. Verify the kernel handles GetAgentReadiness and replies with KernelResponse::AgentReadiness for that request id
  4. If reproducible, file/inspect the BusKernelClient reply-correlation logic for request/response id mismatch

Example fix

// before: mismatched versions cause stale response enum
// after: pin both crates to the same workspace/version and rebuild
cargo build -p astrid-gateway -p astrid-daemon-kernel
Defensive patterns

Strategy: try-catch

Validate before calling

match kernel_response {
    KernelResponse::AgentReadiness(_) | KernelResponse::Error(_) => Ok(()),
    other => Err(format!("kernel version mismatch: got {other:?} for GetAgentReadiness")),
}

Type guard

fn is_agent_readiness(resp: &KernelResponse) -> bool {
    matches!(resp, KernelResponse::AgentReadiness(_))
}

Try / catch

match client.request(KernelRequest::GetAgentReadiness).await? {
    KernelResponse::AgentReadiness(r) => Ok(r),
    KernelResponse::Error(msg) => Err(GatewayError::Forbidden { reason: msg }),
    other => { log::error!("kernel protocol mismatch: {other:?}"); Err(GatewayError::Internal(anyhow::anyhow!("unexpected response for GetAgentReadiness: {other:?}"))) }
}

Prevention

When it happens

Trigger: get_readiness() in crates/astrid-gateway/src/routes/system.rs sends KernelRequest::GetAgentReadiness and matches only KernelResponse::AgentReadiness(r) and KernelResponse::Error(msg); any other variant (e.g. Success(_), a mismatched reply from a different request type) hits the catch-all arm.

Common situations: A kernel upgrade changed the response enum and the gateway was not rebuilt; requests are being routed to the wrong kernel handler; a bus reply for a different request id is delivered to this awaiting caller due to a client-side multiplexing bug.

Related errors


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

Appendix: source

Thrown at crates/astrid-gateway/src/routes/system.rs:72

        (status = 200, description = "`AgentLoopReadiness` JSON shape: `{ ready: bool, prompt_subscribers: [string], response_publishers: [string], unsatisfied_required_imports: [{ capsule, namespace, interface, requirement }], loaded_capsules: [string] }`. `ready` is false when the installed capsule set can't serve an agent chat turn.", content_type = "application/json"),
        (status = 401, body = ErrorBody),
        (status = 403, body = ErrorBody, description = "Caller lacks `capsule:list`."),
    )
)]
pub async fn get_readiness(
    State(state): State<Arc<GatewayState>>,
    req: Request<axum::body::Body>,
) -> GatewayResult<Json<AgentLoopReadiness>> {
    let caller = caller_from(&req)?.clone();
    let client = state.kernel_client_for(&caller)?;
    let resp = client
        .request(KernelRequest::GetAgentReadiness)
        .await
        .map_err(daemon_kernel_error)?;
    match resp {
        KernelResponse::AgentReadiness(r) => Ok(Json(r)),
        KernelResponse::Error(msg) => Err(GatewayError::Forbidden { reason: msg }),
        other => Err(GatewayError::Internal(anyhow::anyhow!(
            "unexpected response for GetAgentReadiness: {other:?}"
        ))),
    }
}

#[utoipa::path(
    post,
    path = "/api/sys/capsules/reload",
    tag = "system",
    responses(
        (status = 204, description = "Capsules reloaded."),
        (status = 401, body = ErrorBody),
        (status = 403, body = ErrorBody, description = "Caller lacks `capsule:reload`."),
    )
)]
pub async fn reload_capsules(
    State(state): State<Arc<GatewayState>>,
    req: Request<axum::body::Body>,

View on GitHub (pinned to affd8760f4)