{"record":{"id":"862e62ea6e8af36f","repo":"openai/codex","slug":"brokenpipe-862e62","errorCode":"BrokenPipe","errorMessage":"unknown process","messagePattern":"unknown process","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"codex-rs/rmcp-client/src/executor_process_transport.rs","lineNumber":253,"sourceCode":"        &mut self,\n        item: TxJsonRpcMessage<RoleClient>,\n    ) -> impl Future<Output = std::result::Result<(), Self::Error>> + Send + 'static {\n        let process = Arc::clone(&self.process);\n        let stdin_write_semaphore = Arc::clone(&self.stdin_write_semaphore);\n        async move {\n            let _stdin_write_permit = stdin_write_semaphore\n                .acquire()\n                .await\n                .map_err(io::Error::other)?;\n            // rmcp hands us a structured JSON-RPC message. Stdio transport on\n            // the wire is JSON plus one newline delimiter.\n            let mut bytes = to_vec(&item).map_err(io::Error::other)?;\n            bytes.push(b'\\n');\n            let response = process.write(bytes).await.map_err(io::Error::other)?;\n            match response.status {\n                WriteStatus::Accepted => Ok(()),\n                WriteStatus::UnknownProcess => {\n                    Err(io::Error::new(io::ErrorKind::BrokenPipe, \"unknown process\"))\n                }\n                WriteStatus::StdinClosed => {\n                    Err(io::Error::new(io::ErrorKind::BrokenPipe, \"stdin closed\"))\n                }\n                WriteStatus::Starting => Err(io::Error::new(\n                    io::ErrorKind::WouldBlock,\n                    \"process is starting\",\n                )),\n            }\n        }\n    }\n\n    fn receive(&mut self) -> impl Future<Output = Option<RxJsonRpcMessage<RoleClient>>> + Send {\n        self.receive_message()\n    }\n\n    async fn close(&mut self) -> std::result::Result<(), Self::Error> {\n        self.process.terminate().await.map_err(io::Error::other)?;","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/rmcp-client/src/executor_process_transport.rs#L235-L271","documentation":"ExecutorProcessTransport.send serializes an MCP message and writes it to a managed child process via a process registry. When the registry answers WriteStatus::UnknownProcess - the targeted process ID is not currently registered - send maps it to ErrorKind::BrokenPipe with message 'unknown process', signalling the write targeted a process the registry no longer knows.","triggerScenarios":"Calling send() on a transport after the target process was removed from (or never registered in) the registry: the process restarted with a new ID, the registry was cleared, or a stale transport handle was retained across a reconnect.","commonSituations":"Caching an ExecutorProcessTransport across a child restart; races between process teardown and in-flight sends; reconnect logic that reuses the pre-restart transport.","solutions":["Treat BrokenPipe 'unknown process' as terminal for the transport: drop it and build a new one bound to the current process","Re-resolve the process ID from the registry before retrying the send","Do not cache transports across restarts - re-create them on process lifecycle events"],"exampleFix":"// before\ntransport.send(item).await?; // BrokenPipe: unknown process\n// after\nmatch transport.send(item).await {\n    Ok(()) => {}\n    Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {\n        let transport = rebuild_transport_for_current_process().await?;\n        transport.send(item).await?;\n    }\n    Err(e) => return Err(e),\n}","handlingStrategy":"fallback","validationCode":"// if the registry is reachable, confirm the target exists before sending:\nif !registry.contains(process_id) {\n    return Err(stale_transport(process_id));\n}","typeGuard":null,"tryCatchPattern":"match transport.send(item).await {\n    Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe\n        && e.to_string().contains(\"unknown process\") =>\n    {\n        // stale transport: rebuild against the live registry and resend once\n    }\n    other => other,\n}","preventionTips":["Re-create transports whenever the underlying process restarts","Subscribe to process lifecycle events instead of discovering staleness via failed sends","Keep MCP requests idempotent so one resend after rebuild is safe"],"tags":["rust","mcp","transport","process-management"],"backgroundTag":"broken-pipe","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}