{"record":{"id":"8d2dfea9a3890abe","repo":"xai-org/grok-build","slug":"transport-is-closed","errorCode":null,"errorMessage":"transport is closed","messagePattern":"transport is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-mcp/src/servers.rs","lineNumber":2519,"sourceCode":"    W: AsyncWrite + Send + Unpin + 'static,\n{\n    type Error = std::io::Error;\n\n    fn send(\n        &mut self,\n        item: TxJsonRpcMessage<RoleClient>,\n    ) -> impl Future<Output = Result<(), Self::Error>> + Send + 'static {\n        let lock = self.write.clone();\n        async move {\n            let mut bytes = serde_json::to_vec(&item).map_err(std::io::Error::other)?;\n            bytes.push(b'\\n');\n            let mut guard = lock.lock().await;\n            match guard.as_mut() {\n                Some(write) => {\n                    write.write_all(&bytes).await?;\n                    write.flush().await\n                }\n                None => Err(std::io::Error::new(\n                    std::io::ErrorKind::NotConnected,\n                    \"transport is closed\",\n                )),\n            }\n        }\n    }\n\n    async fn receive(&mut self) -> Option<RxJsonRpcMessage<RoleClient>> {\n        loop {\n            let mut line = Vec::new();\n            match self.read.read_until(b'\\n', &mut line).await {\n                Ok(0) => return None, // genuine end-of-stream\n                Ok(_) => {}\n                Err(e) => {\n                    tracing::debug!(\n                        server = %self.server_name,\n                        error = %e,\n                        \"MCP stdio read error; closing transport\",","sourceCodeStart":2501,"sourceCodeEnd":2537,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-mcp/src/servers.rs#L2501-L2537","documentation":"An io::Error of kind NotConnection (NotConnected) returned when a write is attempted on an MCP transport whose writer half has already been closed or dropped. The guarded lock holds Option<Write>; None means the transport was shut down. It surfaces to callers of `start`-related transport write paths.","triggerScenarios":"Writing to the MCP transport after the server/session was shut down, after the peer dropped the connection, or before the writer was installed (start not completed).","commonSituations":"Sending a response/notification after the client disconnected; racing shutdown with an in-flight write; reusing a session handle after close.","solutions":["Check session/transport state before writing; stop sending after shutdown is initiated","Re-create the transport/session via start and retry the message","Treat NotConnected as terminal and tear down the session cleanly","Fix shutdown ordering so writers are closed only after pending sends complete"],"exampleFix":"// before\ntransport.write_all(&msg).await?; // NotConnected after shutdown\n// after\nif transport.is_open() {\n    transport.write_all(&msg).await?;\n} else {\n    return Err(SessionError::Closed);\n}","handlingStrategy":"try-catch","validationCode":"// check transport liveness before writing\nif !session.transport_is_open() {\n    return Err(std::io::Error::new(std::io::ErrorKind::NotConnected, \"transport is closed\"));\n}","typeGuard":"fn is_writable(t: &Option<Transport>) -> bool {\n    matches!(t, Some(w) if !w.is_closed())\n}","tryCatchPattern":"match write_all(&bytes).await {\n    Err(e) if e.kind() == std::io::ErrorKind::NotConnected => {\n        // transport shut down; restart via start() or drop session\n    }\n    other => other?,\n}","preventionTips":["Track session shutdown state and cancel pending sends on close","Close transports only after all queued writes flush","Avoid holding session handles past their shutdown","Add connection-state assertions in debug builds"],"tags":["mcp","transport","io","async"],"backgroundTag":"transport-closed","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}