{"record":{"id":"346fd1db0d729704","repo":"astrid-runtime/astrid","slug":"gateway-is-not-wired-to-a-live-event-bus-agent-in","errorCode":null,"errorMessage":"gateway is not wired to a live event bus; agent invocation unavailable","messagePattern":"gateway is not wired to a live event bus; agent invocation unavailable","errorType":"http","errorClass":"GatewayError::Internal","httpStatus":500,"severity":"error","filePath":"crates/astrid-gateway/src/routes/agent.rs","lineNumber":212,"sourceCode":"#[utoipa::path(\n    post,\n    path = \"/api/agent/prompt\",\n    tag = \"agent\",\n    request_body = PromptRequest,\n    responses(\n        (status = 200, description = \"Server-Sent Events stream of agent output. `event: ready` first, then `event: delta` chunks and/or `event: response` for the final reply. Stream closes on `response` or client disconnect.\", 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 post_prompt(\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\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; agent invocation unavailable\"\n        )));\n    };\n\n    let body: PromptRequest = crate::routes::principals::read_json_body(req).await?;\n    let session_id = body\n        .session_id\n        .filter(|s| !s.trim().is_empty())\n        .unwrap_or_else(|| Uuid::new_v4().to_string());\n\n    // Fail fast on an unconfigured agent loop. A daemon whose loaded capsule\n    // set has no prompt subscriber / response publisher (or an unsatisfied\n    // required import) would otherwise emit `ready`, wait out the 5-minute\n    // timeout, and close empty — the client gets no signal. So when the loop\n    // is definitively NOT ready, return a single `error` SSE event and close\n    // immediately.\n    //\n    // Readiness is read from the in-process probe the daemon wired in: it","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/routes/agent.rs#L194-L230","documentation":"The POST /agent prompt SSE handler (post_prompt) requires a live event bus to dispatch the prompt and stream agent events. This error is thrown when GatewayState.event_bus is None, meaning the gateway was built without a bus. It is a defensive check: without a bus the request cannot be forwarded to the agent runtime.","triggerScenarios":"Sending a prompt request to the agent prompt route when the gateway was constructed with event_bus = None (e.g. started in a reduced/no-agent mode or a wiring bug omitted the bus).","commonSituations":"Deploying with a config that disables the agent/event-bus subsystem while clients still call the prompt endpoint; a startup ordering or DI bug where the bus was never injected into GatewayState; running a stripped build (e.g. tests or local mode) against agent routes.","solutions":["Wire a live event bus into GatewayState when constructing the gateway (pass the bus instance at build time)","Enable the agent/event-bus subsystem in the gateway configuration and restart","If the deployment intentionally has no agent support, stop calling the agent prompt route (return 404/route it away)","Check the build/router assembly code path to ensure event_bus is set before serving"],"exampleFix":"// before: bus never provided\nlet state = GatewayState { store, event_bus: None };\n// after\nlet bus = EventBus::connect(&config.bus_endpoint).await?;\nlet state = GatewayState { store, event_bus: Some(Arc::new(bus)) };","handlingStrategy":"validation","validationCode":"// Rust: check before calling the prompt endpoint / building the state\npub fn ensure_agent_ready(state: &GatewayState) -> Result<(), GatewayError> {\n    if state.event_bus.is_none() {\n        return Err(GatewayError::Internal(anyhow::anyhow!(\n            \"event bus not wired; agent routes disabled\"\n        )));\n    }\n    Ok(())\n}","typeGuard":"fn has_event_bus(state: &GatewayState) -> bool {\n    matches!(state.event_bus, Some(_))\n}","tryCatchPattern":"match post_prompt(state, req).await {\n    Err(GatewayError::Internal(e)) if e.to_string().contains(\"not wired to a live event bus\") => {\n        StatusCode::SERVICE_UNAVAILABLE // or 503 with agent-disabled hint\n    }\n    other => other.map(Into::into),\n}","preventionTips":["Assert event_bus.is_some() at startup and refuse to serve agent routes otherwise","Keep deployment config and exposed routes in sync: if no bus, don't expose /agent","Add a health endpoint reporting event-bus connectivity","Cover GatewayState construction with a test that fails when agent routes exist but the bus is None"],"tags":["event-bus","misconfiguration","agent","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"}