{"record":{"id":"ef5bff0123a83b34","repo":"zed-industries/zed","slug":"failed-to-send-message-error","errorCode":null,"errorMessage":"failed to send message: {error}","messagePattern":"failed to send message: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/remote/src/remote_client.rs","lineNumber":1985,"sourceCode":"    fn send<T: EnvelopedMessage>(&self, payload: T) -> Result<()> {\n        log::debug!(\"remote send name:{}\", T::NAME);\n        self.send_dynamic(payload.into_envelope(0, None, None))\n    }\n\n    fn request_dynamic(\n        &self,\n        mut envelope: proto::Envelope,\n        type_name: &'static str,\n        use_buffer: bool,\n    ) -> impl 'static + Future<Output = Result<proto::Envelope>> {\n        envelope.id = self.next_message_id.fetch_add(1, SeqCst);\n        let (tx, rx) = oneshot::channel();\n        let mut response_channels_lock = self.response_channels.lock();\n        response_channels_lock.insert(MessageId(envelope.id), tx);\n        drop(response_channels_lock);\n\n        let result = if use_buffer {\n            self.send_buffered(envelope)\n        } else {\n            self.send_unbuffered(envelope)\n        };\n        async move {\n            if let Err(error) = &result {\n                log::error!(\"failed to send message: {error}\");\n                anyhow::bail!(\"failed to send message: {error}\");\n            }\n\n            let response = rx.await.context(\"connection lost\")?.0;\n            if let Some(proto::envelope::Payload::Error(error)) = &response.payload {\n                return Err(RpcError::from_proto(error, type_name));\n            }\n            Ok(response)\n        }\n    }\n\n    fn request_stream_dynamic(","sourceCodeStart":1967,"sourceCodeEnd":2003,"githubUrl":"https://github.com/zed-industries/zed/blob/5a9b9558db01a6b906cec2fb70a797affdc58cdd/crates/remote/src/remote_client.rs#L1967-L2003","documentation":"request_dynamic registers a response channel, then sends the envelope either buffered (while disconnected) or unbuffered. If the underlying send fails - the outgoing channel was closed because the connection handler shut down (crates/remote/src/remote_client.rs:1964) - it logs 'failed to send message' and bails with the transport error.","triggerScenarios":"Issuing any RPC request() at the moment the connection drops: the multiplex/connection task has exited and the outgoing sink is closed, so send_buffered() or send_unbuffered() returns Err.","commonSituations":"Network blip between send setup and flush; remote server process died; requests racing the disconnect/reconnect transition.","solutions":["Retry the request after the client reconnects (reconnect replays buffered envelopes where applicable)","Check the connection state before issuing non-idempotent RPCs","If sends fail persistently while supposedly connected, restart the remote server on the host"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Gate RPCs on a live connection before issuing them\nif !client.is_connected() {\n    client.wait_until_connected(cx).await; // or trigger/await reconnect\n}\nclient.request(proto::Ping {}).await?;","typeGuard":null,"tryCatchPattern":"match client.request(payload.clone()).await {\n    Err(e) if e.to_string().contains(\"failed to send message\") => {\n        // connection dropped mid-send: wait for reconnect, then re-issue once\n        client.wait_until_connected(cx).await;\n        client.request(payload).await\n    }\n    other => other,\n}","preventionTips":["Route all requests through one supervisor that gates on connection state","Make RPCs idempotent so a replay after reconnect is safe"],"tags":["remote","rpc","send","connection-closed","network"],"backgroundTag":"send-on-closed-connection","analyzedSha":"5a9b9558db01a6b906cec2fb70a797affdc58cdd","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}