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

unexpected response for GetStatus

Error message

unexpected response for GetStatus: {other:?}

What it means

`get_status` in `system.rs` sends `KernelRequest::GetStatus` over the bus with `daemon_kernel_error` error mapping, then matches the `KernelResponse`. If the kernel replies with anything other than `Status` or `Error` — a protocol mismatch — the catch-all arm raises a 500 `GatewayError::Internal` with "unexpected response for GetStatus". It signals the gateway and kernel disagree on the response protocol.

Solutions

  1. Read the `{other:?}` payload in logs to identify the unexpected `KernelResponse` variant.
  2. Redeploy gateway and kernel from matching versions.
  3. Add an explicit match arm for the observed variant in `get_status`.
  4. Check the kernel logs for the corresponding GetStatus handling to find the source of the mismatched reply.

Example fix

// before
other => Err(GatewayError::Internal(anyhow::anyhow!(
    "unexpected response for GetStatus: {other:?}"
))),
// after
KernelResponse::StatusV2(s) => Ok(Json(Status::from(s))),
other => Err(GatewayError::Internal(anyhow::anyhow!(
    "unexpected response for GetStatus: {other:?}"
))),
Defensive patterns

Strategy: type-guard

Type guard

fn as_status(resp: KernelResponse) -> Option<Status> {
    match resp { KernelResponse::Status(s) => Some(s), _ => None }
}

Try / catch

match status.get_status().await {
    Ok(s) => s,
    Err(e) if e.message.starts_with("unexpected response for GetStatus") => {
        // protocol mismatch: check versions, retry after redeploy
        Err(ProtocolMismatch)
    }
    Err(GatewayError::Forbidden { reason }) => return Err(reason.into()),
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: GET on the system status route when the kernel returns a `KernelResponse` variant that is neither `Status(_)` nor `Error(_)` (e.g. a variant added in a newer kernel, or a mismatched reply type routed back).

Common situations: Mixed-version deployment where the kernel emits a new response variant the gateway binary doesn't know; a routing bug delivering another request's response to the status caller; hand-edited or stale protocol definitions on one side.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

        (status = 200, description = "`DaemonStatus` JSON shape: `{ pid, started_at, uptime_secs, active_connections, ephemeral, capsules: { loaded, failed }, session_id }`.", content_type = "application/json"),
        (status = 401, body = ErrorBody),
        (status = 403, body = ErrorBody, description = "Caller lacks `system:status`."),
    )
)]
pub async fn get_status(
    State(state): State<Arc<GatewayState>>,
    req: Request<axum::body::Body>,
) -> GatewayResult<Json<DaemonStatus>> {
    let caller = caller_from(&req)?.clone();
    let client = state.kernel_client_for(&caller)?;
    let resp = client
        .request(KernelRequest::GetStatus)
        .await
        .map_err(daemon_kernel_error)?;
    match resp {
        KernelResponse::Status(s) => Ok(Json(s)),
        KernelResponse::Error(msg) => Err(GatewayError::Forbidden { reason: msg }),
        other => Err(GatewayError::Internal(anyhow::anyhow!(
            "unexpected response for GetStatus: {other:?}"
        ))),
    }
}

#[utoipa::path(
    get,
    path = "/api/sys/readiness",
    tag = "system",
    responses(
        (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>,

View on GitHub (pinned to affd8760f4)