{"record":{"id":"9b4abbdc21012f80","repo":"github/copilot-sdk","slug":"writer-actor-has-shut-down","errorCode":null,"errorMessage":"writer actor has shut down","messagePattern":"writer actor has shut down","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"rust/src/jsonrpc.rs","lineNumber":663,"sourceCode":"    /// # Cancel safety\n    ///\n    /// **Cancel-safe.** Pre-serializes the body, enqueues it on the writer\n    /// actor's command channel, and awaits an ack. Caller cancellation\n    /// drops the ack receiver; the actor still completes the frame and\n    /// flushes. A partial frame can never appear on the wire.\n    pub async fn write<T: serde::Serialize>(&self, message: &T) -> Result<(), Error> {\n        let body = serde_json::to_vec(message)?;\n        let mut frame = Vec::with_capacity(CONTENT_LENGTH_HEADER.len() + 16 + body.len() + 4);\n        frame.extend_from_slice(CONTENT_LENGTH_HEADER.as_bytes());\n        frame.extend_from_slice(body.len().to_string().as_bytes());\n        frame.extend_from_slice(b\"\\r\\n\\r\\n\");\n        frame.extend_from_slice(&body);\n\n        let (ack_tx, ack_rx) = oneshot::channel();\n        self.write_tx\n            .send(WriteCommand { frame, ack: ack_tx })\n            .map_err(|_| {\n                Error::from(std::io::Error::new(\n                    std::io::ErrorKind::BrokenPipe,\n                    \"writer actor has shut down\",\n                ))\n            })?;\n\n        match ack_rx.await {\n            Ok(Ok(())) => Ok(()),\n            Ok(Err(e)) => Err(Error::from(e)),\n            Err(_) => Err(Error::from(std::io::Error::new(\n                std::io::ErrorKind::BrokenPipe,\n                \"writer actor dropped ack without responding\",\n            ))),\n        }\n    }\n}\n\n/// RAII guard that removes a pending-request entry from the map if the\n/// owning future is dropped before the response arrives. Disarmed on the","sourceCodeStart":645,"sourceCodeEnd":681,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/rust/src/jsonrpc.rs#L645-L681","documentation":"Client::write sends a serialized frame to the dedicated writer actor over a channel. If the writer task has already terminated (connection closed or being torn down), the send fails and the library converts it into a BrokenPipe io::Error so callers see a familiar std::io-style failure rather than a channel-send panic.","triggerScenarios":"Calling any public write/RPC method on a JsonRpc client whose background writer actor has exited — e.g. after the peer closed the connection, after Client::stop, or after the writer task panicked on an underlying I/O error.","commonSituations":"Sending a request over a socket the server already closed; racing a write against Client::stop during shutdown; long-lived connections dropped by idle timeouts or network interruption.","solutions":["Check connection liveness and reconnect the client before retrying the write","Ensure Client::stop is not called concurrently with in-flight writes; await pending operations first","Handle BrokenPipe by recreating the Client (transport and writer actor) instead of reusing it","Inspect writer task logs for an earlier underlying I/O error that killed the actor"],"exampleFix":"// before\nclient.write(request).await?; // panics/fails after shutdown\n// after\nif client.is_running() {\n    client.write(request).await?;\n} else {\n    client = Client::connect(endpoint).await?;\n    client.write(request).await?;\n}","handlingStrategy":"try-catch","validationCode":"// Rust\nfn can_write(client: &Client) -> bool { client.is_running() } // or track shutdown state yourself","typeGuard":null,"tryCatchPattern":"// Rust\nmatch client.write(frame).await {\n    Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => reconnect_and_retry().await?,\n    other => other?,\n}","preventionTips":["Await in-flight writes before calling Client::stop","Recreate the Client after any BrokenPipe instead of reusing it","Monitor connection state and reconnect proactively on peer disconnect"],"tags":["rust","ipc","broken-pipe","shutdown"],"backgroundTag":"broken-pipe","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}