{"record":{"id":"3b36df18cd13d38d","repo":"astrid-runtime/astrid","slug":"gateway-is-not-wired-to-a-live-event-bus-live-fee","errorCode":null,"errorMessage":"gateway is not wired to a live event bus; live feed unavailable","messagePattern":"gateway is not wired to a live event bus; live feed unavailable","errorType":"http","errorClass":"GatewayError::Internal","httpStatus":500,"severity":"error","filePath":"crates/astrid-gateway/src/routes/stream.rs","lineNumber":123,"sourceCode":"#[utoipa::path(\n    get,\n    path = \"/api/agent/stream\",\n    tag = \"agent\",\n    responses(\n        (status = 200, description = \"Long-lived Server-Sent Events feed of the caller's own conversation activity across all threads. `event: ready` first, then `event: agent` (in-flight turn events) and `event: session_event` (thread lifecycle). Cross-principal events are never delivered.\", content_type = \"text/event-stream\"),\n        (status = 401, body = ErrorBody, description = \"Missing / invalid bearer.\"),\n        (status = 500, body = ErrorBody, description = \"Gateway not wired to a live event bus.\"),\n    )\n)]\npub async fn get_stream(\n    State(state): State<Arc<GatewayState>>,\n    req: Request<axum::body::Body>,\n) -> GatewayResult<Sse<impl Stream<Item = Result<Event, Infallible>>>> {\n    let caller = caller_from(&req)?;\n    metrics::counter!(\"astrid_gateway_agent_stream_total\").increment(1);\n\n    let Some(bus) = state.event_bus.clone() else {\n        return Err(GatewayError::Internal(anyhow::anyhow!(\n            \"gateway is not wired to a live event bus; live feed unavailable\"\n        )));\n    };\n\n    let principal = caller.principal.to_string();\n\n    // Subscribe FIRST, both routes scoped to the caller's principal. The\n    // outer `Some` marks the route as scoped; the inner `Some(principal)` is\n    // the security boundary — a foreign-principal event is dropped at\n    // enqueue and never enters this route's budget (see module docs). A\n    // fresh per-call UUID isolates this connection's routes from any other\n    // live feed.\n    let conn_uuid = Uuid::new_v4();\n    let scope = Some(Some(principal.clone()));\n    let mut agent_rx = bus.subscribe_topic_routed_scoped(\n        conn_uuid,\n        TOPIC_AGENT_EVENTS,\n        \"gateway\",","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/routes/stream.rs#L105-L141","documentation":"The public SSE `get_stream` route in `stream.rs` clones the event bus from `GatewayState` and returns a 500 `GatewayError::Internal` with \"gateway is not wired to a live event bus; live feed unavailable\" when it is absent. The live event feed requires a daemon-connected gateway; without a bus there is no stream source. Like the sessions variant, this is a server wiring condition.","triggerScenarios":"A client opens the SSE stream endpoint (after passing `caller_from` auth) while `state.event_bus` is `None` because the gateway runs without a co-located daemon or bus init failed.","commonSituations":"Pointing clients at a standalone/headless gateway instance for live events; event bus initialization silently failing at startup; environments where streaming endpoints are exposed but daemon wiring is disabled.","solutions":["Start the gateway with a live event bus (co-located daemon) so `state.event_bus` is set.","Check gateway startup logs for event-bus initialization errors and fix the wiring config.","Point the SSE client at a daemon-connected gateway instance.","Return a clearer 503/disabled response for streaming routes when no bus is configured."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// probe the stream endpoint's precondition: only subscribe on bus-connected gateways\nif (!gatewayMetadata.hasEventBus) {\n  throw new Error(\"live feed unavailable: gateway is not daemon-connected\");\n}","typeGuard":null,"tryCatchPattern":"const es = new EventSource(url);\nes.onerror = (ev) => {\n  // server returned 500 'not wired to a live event bus' -> backoff and re-discover a bus-connected gateway\n  scheduleReconnectWithFallback();\n};","preventionTips":["Only point SSE clients at gateways advertised as daemon-connected (service tags/health metadata).","Surface bus absence as a 503 with a machine-readable code so clients can distinguish it from bugs.","Alert on event-bus init failures at gateway startup.","Implement client-side fallback (polling) when the live feed is unavailable."],"tags":["gateway","sse","event-bus","deployment"],"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-14T05:17:10.506Z"}