{"record":{"id":"666557636ec7ba05","repo":"astrid-runtime/astrid","slug":"gateway-is-not-wired-to-a-live-event-bus-session","errorCode":null,"errorMessage":"gateway is not wired to a live event bus; session threads unavailable","messagePattern":"gateway is not wired to a live event bus; session threads unavailable","errorType":"http","errorClass":"GatewayError::Internal","httpStatus":500,"severity":"error","filePath":"crates/astrid-gateway/src/routes/sessions.rs","lineNumber":722,"sourceCode":"        &response_topic,\n        payload,\n        &correlation_id,\n        &caller.principal,\n        caller.device_key_id.as_deref(),\n        &expected_sources,\n        CAPSULE_TIMEOUT,\n    )\n    .await?;\n\n    let parsed = parse_search_response(value)?;\n    Ok(Json(parsed))\n}\n\n/// Pull the live event bus out of state, or fail with a 500 the same\n/// way `agent.rs` does when the gateway isn't co-located with a daemon.\nfn require_bus(state: &GatewayState) -> GatewayResult<Arc<EventBus>> {\n    state.event_bus.clone().ok_or_else(|| {\n        GatewayError::Internal(anyhow::anyhow!(\n            \"gateway is not wired to a live event bus; session threads unavailable\"\n        ))\n    })\n}\n\n/// Gate the list route on the session `list` capability being present in the\n/// caller principal's loaded capsule view, so a mixed 1.0/1.1 fleet behaves\n/// honestly: a pre-1.1 session capsule yields `NotImplemented` (501) on the\n/// 1.1 thread-management routes (`list` / `get_meta` / `update` / `delete` /\n/// `search`) instead of waiting out the bus timeout on a verb nobody handles.\n/// It probes the `list` verb specifically as a proxy — the whole 1.1 verb set\n/// ships in one capsule, so a `list` handler implies all of them.\n///\n/// Uses the in-process [`CapsuleTopicProbe`] — a cap-free read of the live\n/// registry, the same approach `POST /api/agent/prompt` takes for its\n/// fail-fast. The check is principal-scoped because default is not a shared\n/// fallback: a loaded default session capsule says nothing about whether the\n/// caller's own runtime has finished async warm-up. The probe is required so","sourceCodeStart":704,"sourceCodeEnd":740,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/routes/sessions.rs#L704-L740","documentation":"`require_bus` in `sessions.rs` pulls the live `EventBus` out of `GatewayState` and fails with a 500 `GatewayError::Internal` when `state.event_bus` is `None`, mirroring the behavior of `agent.rs`. Session routes need the bus to read/write session threads, so a gateway not co-located with a daemon cannot serve them. This is a deployment/wiring condition, not a client error.","triggerScenarios":"Any session route (`list_sessions_inner`, `get_session_messages_inner`, `get_session_inner`, `update_session_inner`, `delete_session_inner`, `search_sessions_inner`) calls `require_bus(state)` while `state.event_bus` is `None` — i.e. the gateway was started without a daemon/event-bus connection.","commonSituations":"Running the gateway standalone (no co-located daemon) and then hitting /sessions endpoints; the event bus failed to initialize at startup so `event_bus` stayed `None`; configuration that disables daemon wiring in an environment where session APIs are still used.","solutions":["Start/co-locate the astrid daemon so the gateway's `event_bus` is populated at startup.","Verify gateway startup config actually wires the event bus (check startup logs for bus initialization).","Route session-thread traffic to a gateway instance that is daemon-connected.","If standalone operation is intended, hide/disable the session routes rather than serving 500s."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before calling any /sessions route, confirm the gateway has a bus\nlet has_bus = gateway_state.event_bus.is_some();\nif !has_bus { return Err(\"gateway has no event bus; session routes unavailable\".into()); }","typeGuard":null,"tryCatchPattern":"match sessions.list().await {\n    Ok(page) => page,\n    Err(e) if e.message.contains(\"not wired to a live event bus\") => {\n        // deployment issue: reroute to daemon-connected gateway or surface clear error\n        Err(SessionUnavailable)\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Add a readiness probe that fails until `event_bus` is wired, and route traffic only to ready instances.","Co-locate the daemon with any gateway that serves session routes.","Log a loud startup warning when session routes are registered without a bus.","Separate standalone gateways (no daemon) from daemon-connected ones in service discovery."],"tags":["gateway","event-bus","deployment","internal-error"],"backgroundTag":"internal-invariant-violation","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}