{"record":{"id":"4495503b3bad9446","repo":"astrid-runtime/astrid","slug":"mcp-gateway-is-shutting-down","errorCode":null,"errorMessage":"MCP gateway is shutting down","messagePattern":"MCP gateway is shutting down","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/astrid-cli/src/commands/mcp/gateway.rs","lineNumber":118,"sourceCode":"            permits: Mutex::new(HashMap::new()),\n            slots: Mutex::new(HashMap::new()),\n            admission: Arc::new(Mutex::new(())),\n            initialize: Mutex::new(()),\n            shutdown: CancellationToken::new(),\n            active_connections: AtomicUsize::new(0),\n            connections_drained: Notify::new(),\n            watchers: Mutex::new(Vec::new()),\n            shutdown_result: Mutex::new(None),\n            shutdown_finished: Notify::new(),\n            shutdown_ack_sent: Notify::new(),\n            stop_ack_waiters: AtomicUsize::new(0),\n        }\n    }\n\n    /// Get or establish the one daemon uplink for `principal`.\n    async fn client_for(&self, principal: &astrid_core::PrincipalId) -> Result<Client> {\n        if self.shutdown.is_cancelled() {\n            anyhow::bail!(\"MCP gateway is shutting down\");\n        }\n        let key = principal.to_string();\n        if let Some(client) = self.clients.lock().await.get(&key).cloned() {\n            return Ok(client);\n        }\n\n        // Serialize first-use handshakes so two simultaneous attaches for a\n        // new principal cannot create duplicate long-lived uplinks/watchers.\n        let _initialize = self.initialize.lock().await;\n        if self.shutdown.is_cancelled() {\n            anyhow::bail!(\"MCP gateway is shutting down\");\n        }\n        if let Some(client) = self.clients.lock().await.get(&key).cloned() {\n            return Ok(client);\n        }\n\n        let session = astrid_core::SessionId::from_uuid(Uuid::new_v4());\n        let mut client = crate::socket_client::connect_for_workspace(","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/mcp/gateway.rs#L100-L136","documentation":"The MCP gateway is shutting down (its shutdown token is cancelled). client_for checks this before serving a principal's uplink and refuses to create or hand out clients, so in-flight requests fail fast instead of hanging against a dying gateway.","triggerScenarios":"Calling verify_uplink (or any operation needing an uplink) while the gateway is concurrently shutting down; e.g. a request racing a `Ctrl-C`/daemon stop, or the first-use handshake attempted after cancellation.","commonSituations":"Cancelling the gateway while a client still issues requests; shutdown triggered by timeout or supervisor stop; long-running attach outliving the gateway process.","solutions":["Retry the operation after the gateway restarts","Restart the MCP gateway and re-run `aos mcp ready` + attach","Treat this as expected during shutdown and stop issuing new requests"],"exampleFix":"// before\nlet client = gateway.client_for(&principal).await?;\n// after\nmatch gateway.client_for(&principal).await {\n    Ok(c) => ...,\n    Err(e) if e.to_string().contains(\"shutting down\") => /* retry or exit gracefully */,\n    Err(e) => return Err(e),\n}","handlingStrategy":"try-catch","validationCode":"// Poll gateway liveness before issuing requests\nwhile gateway_is_starting_or_restarting() { sleep(BACKOFF).await; }","typeGuard":"fn gateway_alive(g: &Gateway) -> bool { !g.shutdown.is_cancelled() }","tryCatchPattern":"match gateway.client_for(&principal).await {\n    Ok(c) => c,\n    Err(e) if e.to_string().contains(\"shutting down\") => /* stop work gracefully or wait for restart */,\n    Err(e) => return Err(e),\n}","preventionTips":["Drain in-flight requests before signalling shutdown","Suppress new attaches once shutdown begins","Use supervisor health checks to wait for a restarted gateway"],"tags":["mcp","shutdown","concurrency"],"backgroundTag":"invalid-state-transition","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"}