{"record":{"id":"0da3b6a6b569c6dc","repo":"astral-sh/ruff","slug":"received-exit-notification-before-a-shutdown-reque-0da3b6","errorCode":null,"errorMessage":"Received exit notification before a shutdown request","messagePattern":"Received exit notification before a shutdown request","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/ty_server/src/server/main_loop.rs","lineNumber":68,"sourceCode":"                                    &req.method\n                                );\n                                client.respond_err(\n                                    req.id,\n                                    lsp_server::ResponseError {\n                                        code: lsp_server::ErrorCode::InvalidRequest as i32,\n                                        message: \"Shutdown already requested\".to_owned(),\n                                        data: None,\n                                    },\n                                );\n                                continue;\n                            }\n\n                            api::request(req)\n                        }\n                        Message::Notification(notification) => {\n                            if notification.method == lsp_types::ExitNotification::METHOD.as_str() {\n                                if !self.session.is_shutdown_requested() {\n                                    return Err(anyhow!(\n                                        \"Received exit notification before a shutdown request\"\n                                    ));\n                                }\n\n                                tracing::debug!(\"Received exit notification, exiting\");\n                                return Ok(());\n                            }\n\n                            api::notification(notification)\n                        }\n\n                        // Handle the response from the client to a server request\n                        Message::Response(response) => {\n                            if let Some(handler) = self\n                                .session\n                                .request_queue_mut()\n                                .outgoing_mut()\n                                .complete(&response.id)","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/astral-sh/ruff/blob/672bb4edf04c84f8b0753346359daee4057158f2/crates/ty_server/src/server/main_loop.rs#L50-L86","documentation":"The client sent the `exit` notification without a prior `shutdown` request, violating the LSP lifecycle; the server returns an error from its main loop (main_loop.rs:68). The spec requires shutdown before exit, and the server exits with a non-zero status in this case.","triggerScenarios":"A client that sends exit directly (skipping the shutdown request), a reconnect/restart routine that exits the old server without shutting it down, or a misimplemented lifecycle in a custom LSP client.","commonSituations":"Buggy or hand-rolled LSP client lifecycle code, editor extensions that 'restart server' by exiting only, or race where the shutdown request was still in flight when exit arrived.","solutions":["Always send the `shutdown` request and await its response before sending `exit`","In restart flows, shut down the old server before spawning the new one","If using a client library, prefer its built-in lifecycle methods over manual messages"],"exampleFix":"// before\nclient.notify('exit');  // exit before shutdown -> error\n\n// after\nawait client.sendRequest('shutdown');\nclient.notify('exit');","handlingStrategy":"fallback","validationCode":"// TS: enforce ordering with a small lifecycle helper\nlet shutdownAcked = false;\nawait client.sendRequest('shutdown');\nshutdownAcked = true;\nclient.notify('exit');","typeGuard":null,"tryCatchPattern":"// TS: if exit raced ahead (e.g. timeout path), restart cleanly rather than reusing state\ntry { await stopServer(client); }\ncatch (e) {\n  log.warn('server exited uncleanly; spawning a fresh instance');\n  await startServer();\n}","preventionTips":["Never send exit without awaiting the shutdown response","Centralize the shutdown/exit pair in one helper so no call site can skip a step","On restart flows, build a new server instance instead of reusing the old connection"],"tags":["lsp","shutdown","lifecycle","exit"],"backgroundTag":null,"analyzedSha":"672bb4edf04c84f8b0753346359daee4057158f2","analyzedAt":"2026-08-16T08:54:05.464Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}