{"record":{"id":"91c5ee77385c09e0","repo":"astral-sh/ruff","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":null,"httpStatus":null,"severity":"warning","filePath":"crates/ruff_server/src/server/main_loop.rs","lineNumber":29,"sourceCode":"    server::{api, schedule},\n    session::Client,\n};\n\npub(crate) type MainLoopSender = crossbeam::channel::Sender<Event>;\npub(crate) type MainLoopReceiver = crossbeam::channel::Receiver<Event>;\n\nimpl Server {\n    pub(super) fn main_loop(&mut self) -> crate::Result<()> {\n        self.initialize(&Client::new(\n            self.main_loop_sender.clone(),\n            self.connection.sender.clone(),\n        ));\n\n        let mut scheduler = schedule::Scheduler::new(self.worker_threads);\n\n        while let Ok(next_event) = self.next_event() {\n            let Some(next_event) = next_event else {\n                anyhow::bail!(\"client exited without proper shutdown sequence\");\n            };\n\n            match next_event {\n                Event::Message(msg) => {\n                    let client = Client::new(\n                        self.main_loop_sender.clone(),\n                        self.connection.sender.clone(),\n                    );\n\n                    let task = match msg {\n                        Message::Request(req) => {\n                            self.session\n                                .request_queue_mut()\n                                .incoming_mut()\n                                .register(req.id.clone(), req.method.clone());\n\n                            if self.session.is_shutdown_requested() {\n                                tracing::warn!(","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/astral-sh/ruff/blob/26f38c119cac42e4d320ba08f09224fdec74af2c/crates/ruff_server/src/server/main_loop.rs#L11-L47","documentation":"The LSP server's event loop (`main_loop`) polls `next_event()`, which returns `Ok(None)` when the message stream ends. If the stream ends without the client sending a proper `exit` notification after `shutdown`, the loop treats this as a protocol violation and aborts with this anyhow bail instead of exiting cleanly. LSP requires clients to follow the `initialize` -> `shutdown` -> `exit` sequence.","triggerScenarios":"The LSP client (editor process) terminates abruptly or closes the stdio/IPC stream without sending the `shutdown` request followed by the `exit` notification; `next_event()` returns None on stream EOF before an Exit event was processed.","commonSituations":"Editor crashes or is force-killed while the Ruff language server is running; a custom or scripted LSP client that stops reading/writing and drops the connection; debugging sessions where the transport is closed manually.","solutions":["Ensure your LSP client sends a `shutdown` request and then an `exit` notification before closing the connection","If you control the client, handle editor/process termination by sending the LSP shutdown/exit sequence in a cleanup handler","Treat this message in server logs as a symptom of abrupt client disconnect, not an internal Ruff bug; restart the server if needed"],"exampleFix":"// custom LSP client cleanup\n// before\nprocess::exit(0);\n// after\nclient.shutdown().await?;\nclient.exit().await; // sends the LSP exit notification\nprocess::exit(0);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match server_exit_result { Err(e) if e.to_string().contains(\"proper shutdown sequence\") => log.warn(\"client disconnected without LSP shutdown/exit\"), other => other? }","preventionTips":["Always send the LSP shutdown request followed by the exit notification before terminating the client","Register cleanup handlers that close the LSP connection gracefully on editor shutdown","Avoid force-killing the editor process while language servers are attached"],"tags":["lsp","shutdown","protocol"],"backgroundTag":"lsp-protocol-violation","analyzedSha":"26f38c119cac42e4d320ba08f09224fdec74af2c","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}