{"record":{"id":"354000bebf9d735b","repo":"rust-lang/rust","slug":"client-exited-without-proper-shutdown-sequence","errorCode":null,"errorMessage":"client exited without proper shutdown sequence","messagePattern":"client exited without proper shutdown sequence","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs","lineNumber":205,"sourceCode":"                .map(|f| format!(\"**/{f}\"));\n            self.register_did_save_capability(additional_patterns);\n        }\n\n        if self.config.discover_workspace_config().is_none() {\n            self.fetch_workspaces_queue.request_op(\n                \"startup\".to_owned(),\n                FetchWorkspaceRequest { path: None, force_crate_graph_reload: false },\n            );\n            if let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) =\n                self.fetch_workspaces_queue.should_start_op()\n            {\n                self.fetch_workspaces(cause, path, force_crate_graph_reload);\n            }\n        }\n\n        while let Ok(event) = self.next_event(&inbox) {\n            let Some(event) = event else {\n                anyhow::bail!(\"client exited without proper shutdown sequence\");\n            };\n            if matches!(\n                &event,\n                Event::Lsp(lsp_server::Message::Notification(Notification { method, .. }))\n                if method == lsp_types::ExitNotification::METHOD.as_str()\n            ) {\n                return Ok(());\n            }\n            self.handle_event(event);\n        }\n\n        Err(anyhow::anyhow!(\"A receiver has been dropped, something panicked!\"))\n    }\n\n    fn register_did_save_capability(&mut self, additional_patterns: impl Iterator<Item = String>) {\n        let additional_filters = additional_patterns.map(|pattern| {\n            lsp_types::DocumentFilter::TextDocumentFilter(lsp_types::TextDocumentFilter::Pattern(\n                lsp_types::TextDocumentFilterPattern {","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/rust-lang/rust/blob/7088e4b63a9516ebfbfe2ab2d999cf01a528ac14/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs#L187-L223","documentation":"Bailed by the main event loop when next_event returns None — the LSP client closed the connection without sending the Exit notification that the loop uses as its normal termination signal. rust-analyzer treats an abrupt close as abnormal because it cannot distinguish it from a crashed client.","triggerScenarios":"The LSP transport's receiver hits EOF (client disconnected) before an Exit notification arrives. Happens when the editor kills the server process, the client crashes, or a transport proxy drops the connection.","commonSituations":"Editor force-killed during shutdown; a buggy client that closes stdin without sending exit; running r-a behind a wrapper that swallowed the exit notification; OS-level process kill.","solutions":["On the client side, always send the `exit` notification after `shutdown` completes, per the LSP spec.","If wrapping r-a in a proxy, forward the exit notification faithfully.","Treat this message in your logs as a client/transport issue, not a rust-analyzer bug — it is r-a reporting the protocol violation."],"exampleFix":"// before: bail on a clean EOF\nlet Some(event) = event else {\n    anyhow::bail!(\"client exited without proper shutdown sequence\");\n};\n\n// after: log and exit cleanly (a missing exit is the client's fault, not fatal for r-a)\nlet Some(event) = event else {\n    tracing::warn!(\"client disconnected without sending `exit`; shutting down\");\n    return Ok(());\n};","handlingStrategy":"try-catch","validationCode":"// Client-side: always perform the LSP shutdown handshake before disconnecting:\nasync fn shutdown_graceful(server: &LspClient) -> Result<(), Box<dyn std::error::Error>> {\n    let _ = server.request(\"shutdown\").await?;\n    server.notify(\"exit\").await?;\n    server.close().await?;\n    Ok(())\n}","typeGuard":"null","tryCatchPattern":"// Server-side: treat a clean EOF as a normal exit rather than an error:\nlet Some(event) = event else {\n    tracing::warn!(\"client disconnected without `exit`; treating as shutdown\");\n    return Ok(());\n};","preventionTips":["Send the `exit` notification after `shutdown` completes (LSP spec).","Forward exit faithfully if you proxy r-a.","Don't kill -9 the server during normal editor close."],"tags":["rust-analyzer","lsp","lifecycle","shutdown"],"backgroundTag":null,"analyzedSha":"7088e4b63a9516ebfbfe2ab2d999cf01a528ac14","analyzedAt":"2026-08-10T14:17:03.603Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}