{"record":{"id":"717fc4c00f1b22ca","repo":"astrid-runtime/astrid","slug":"gateway-is-not-wired-to-a-live-event-bus-audit-st","errorCode":null,"errorMessage":"gateway is not wired to a live event bus; audit stream unavailable","messagePattern":"gateway is not wired to a live event bus; audit stream unavailable","errorType":"http","errorClass":"GatewayError::Internal","httpStatus":500,"severity":"error","filePath":"crates/astrid-gateway/src/routes/events.rs","lineNumber":97,"sourceCode":"    )\n)]\npub async fn get_events(\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)?.clone();\n    let capability_probe = req\n        .extensions()\n        .get::<CapabilityProbe>()\n        .cloned()\n        .unwrap_or_else(CapabilityProbe::deny_all);\n\n    // Without a bus handle (the standalone GatewayState ctor used\n    // by route-level tests), report an honest 502 instead of\n    // hanging — a dashboard would otherwise wait forever on a\n    // stream that can never produce.\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; audit stream unavailable\"\n        )));\n    };\n\n    // The kernel-owned probe applies the caller's live device scope.\n    let initial_firehose = caller_holds(\n        &capability_probe,\n        &caller.principal,\n        caller.device_key_id.as_deref(),\n        AUDIT_FIREHOSE_CAP,\n    );\n\n    // Routed subscription so the audit firehose gets the same\n    // per-(topic, principal) DRR fairness the rest of the gateway\n    // SSE streams now use (#813 Layer 4). The principal-firehose\n    // filter at the post-receive layer is unchanged — it's a\n    // capability gate, not a routing concern.\n    let receiver = bus.subscribe_topic_routed(","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/routes/events.rs#L79-L115","documentation":"get_events streams audit events from the event bus; if the GatewayState has no event bus handle (the standalone constructor used in route-level tests), the route returns a 502-style Internal error immediately instead of hanging. This is an honest failure so dashboards don't wait forever on a stream that can never produce events.","triggerScenarios":"Calling the /events SSE endpoint against a GatewayState built without wiring an event bus (e.g. in tests using the standalone GatewayState constructor, or a deployment where the bus handle was never attached).","commonSituations":"Integration tests constructing GatewayState manually; a misconfigured deployment that skipped bus initialization; refactoring that dropped the bus from state assembly.","solutions":["Wire a live event bus handle into GatewayState before serving requests (state.event_bus = Some(bus))","If this is a route-level test, use the full state constructor that includes the bus, or assert on the 502 behavior","In production, add a startup check that fails fast when the bus is missing"],"exampleFix":"// before\nlet Some(bus) = state.event_bus.clone() else {\n    return Err(GatewayError::Internal(anyhow::anyhow!(\n        \"gateway is not wired to a live event bus; audit stream unavailable\"\n    )));\n};\n// after\nlet bus = state.event_bus.clone().ok_or_else(|| {\n    tracing::error!(\"event bus not configured; cannot serve audit stream\");\n    GatewayError::Internal(anyhow::anyhow!(\n        \"gateway is not wired to a live event bus; audit stream unavailable\"\n    ))\n})?;","handlingStrategy":"try-catch","validationCode":"// Guard before opening the stream\nif state.event_bus.is_none() {\n    return Err(StatusCode::SERVICE_UNAVAILABLE);\n}","typeGuard":"fn has_event_bus(state: &GatewayState) -> bool {\n    state.event_bus.is_some()\n}","tryCatchPattern":"match get_events(state, caller).await {\n    Ok(stream) => sse_response(stream),\n    Err(GatewayError::Internal(e)) if e.to_string().contains(\"not wired to a live event bus\") => {\n        // 502: bus unavailable, do not retry against this instance\n        service_unavailable(\"audit stream unavailable\")\n    }\n    Err(e) => internal_error(e),\n}","preventionTips":["Always construct GatewayState via the full assembler that wires the event bus","Add a startup assertion that event_bus is Some in production builds","Keep a dashboard-side timeout/reconnect policy for SSE streams"],"tags":["rust","event-bus","missing-dependency","gateway","sse"],"backgroundTag":"missing-dependency","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"}