{"record":{"id":"5add9ae61db7d9df","repo":"BoundaryML/baml","slug":"the-connection-with-the-client-has-been-closed","errorCode":null,"errorMessage":"The connection with the client has been closed","messagePattern":"The connection with the client has been closed","errorType":"exception","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"engine/language_server/src/server/connection.rs","lineNumber":172,"sourceCode":"        std::mem::drop(self.receiver);\n        self.threads.into_iter().try_for_each(|t| t.join())?;\n        Ok(())\n    }\n}\n\n/// A weak reference to an underlying sender channel, used for communication with the client.\n/// If the `Connection` that created this `ClientSender` is dropped, any `send` calls will throw\n/// an error.\n#[derive(Clone, Debug)]\npub struct ClientSender {\n    weak_sender: Weak<ConnectionSender>,\n}\n\n// note: additional wrapper functions for senders may be implemented as needed.\nimpl ClientSender {\n    pub(crate) fn send(&self, msg: lsp::Message) -> anyhow::Result<()> {\n        let Some(sender) = self.weak_sender.upgrade() else {\n            anyhow::bail!(\"The connection with the client has been closed\");\n        };\n        sender.send(msg)?;\n        Ok(())\n    }\n}\n","sourceCodeStart":154,"sourceCodeEnd":178,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/language_server/src/server/connection.rs#L154-L178","documentation":"ClientSender holds a weak reference to the channel used to send messages to the LSP client. If the underlying sender has been dropped (the connection was shut down or its thread ended), upgrade() fails and send() bails with this error instead of panicking.","triggerScenarios":"Any server task (diagnostics updates, progress notifications, responses) attempts to send a message after the client connection was closed — e.g. a long-running analysis finishes after the editor disconnected.","commonSituations":"Editor closed while background diagnostics/analysis tasks are still running; server shutting down while queued responses are flushed; test harnesses dropping the client half of the channel early.","solutions":["Drop or cancel background tasks when the connection shuts down so they don't attempt sends.","Check connection liveness (or use a strong sender handle) before sending.","Treat this as a benign shutdown race: log at debug level and ignore, rather than failing the task.","Use a broadcast/closing channel pattern so senders observe disconnect gracefully."],"exampleFix":"// before\nlet Some(sender) = self.weak_sender.upgrade() else {\n    anyhow::bail!(\"The connection with the client has been closed\");\n};\n// after\nlet Some(sender) = self.weak_sender.upgrade() else {\n    log::debug!(\"Client connection closed; dropping outgoing message\");\n    return Ok(());\n};","handlingStrategy":"try-catch","validationCode":"// sender-side liveness check before sending\nif (!clientConnection.isAlive()) return;","typeGuard":"function canSend(sender: Weak<ClientChannel>): boolean {\n  return sender.upgrade().is_some();\n}","tryCatchPattern":"if let Err(e) = sender.send(msg) {\n    log::debug!(\"send failed after disconnect: {e}\"); // tolerate shutdown races\n}","preventionTips":["Cancel background tasks on connection shutdown so they stop sending.","Hold or re-check the strong sender before dispatching messages.","Treat post-disconnect sends as benign; log at debug instead of failing.","Test editor-close scenarios to catch late async sends."],"tags":["lsp","connection","channel"],"backgroundTag":"broken-pipe","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}