astral-sh/ruff · warning
client exited without proper shutdown sequence
Error message
client exited without proper shutdown sequence
What it means
The server's main loop read EOF from the client channel (stdin closed) without ever receiving the `exit` notification, so the server bails with this error instead of shutting down cleanly (main_loop.rs:26). Per the LSP spec, a server whose connection drops before `exit` terminates with an error status.
Source
Thrown at crates/ty_server/src/server/main_loop.rs:26
use lsp_types::Notification;
use lsp_types::Uri;
pub(crate) type ConnectionSender = crossbeam::channel::Sender<Message>;
pub(crate) type MainLoopSender = crossbeam::channel::Sender<Event>;
pub(crate) type MainLoopReceiver = crossbeam::channel::Receiver<Event>;
impl Server {
pub(super) fn main_loop(&mut self) -> crate::Result<()> {
self.initialize(&Client::new(
self.main_loop_sender.clone(),
self.connection.sender.clone(),
));
let mut scheduler = Scheduler::new(self.worker_threads);
while let Ok(next_event) = self.next_event() {
let Some(next_event) = next_event else {
anyhow::bail!("client exited without proper shutdown sequence");
};
let client = Client::new(
self.main_loop_sender.clone(),
self.connection.sender.clone(),
);
match next_event {
Event::Message(msg) => {
let Some(msg) = self.session.should_defer_message(msg) else {
continue;
};
let task = match msg {
Message::Request(req) => {
self.session
.request_queue_mut()
.incoming_mut()View on GitHub (pinned to 672bb4edf0)
Solutions
- If killing the server intentionally, treat this exit as expected — otherwise close with shutdown request + exit notification
- In client code, use the library's graceful dispose (e.g. LanguageClient.stop()) rather than child.kill()
- Check whether the editor crashed; this error is a symptom, not the cause
Example fix
// before
serverProcess.kill(); // stdin drops -> 'client exited without proper shutdown sequence'
// after
await client.sendRequest('shutdown');
client.notify('exit');
await serverProcess.exited; Defensive patterns
Strategy: fallback
Try / catch
// TS: treat abrupt-EOF as a benign abnormal exit when you killed the server
serverProc.on('exit', (code) => {
if (intentionalKill && code !== 0) return; // expected: no shutdown sequence
handleUnexpectedExit(code);
}); Prevention
- Always stop servers via shutdown request + exit notification, even on cleanup paths
- Use the client library's dispose()/stop() instead of child.kill()
- Distinguish intentional kills from crashes before alerting on non-zero exit codes
When it happens
Trigger: The editor or client process is killed (SIGKILL, crash, force-quit), stdin is closed without shutdown/exit, or a client library terminates the child without the LSP shutdown sequence.
Common situations: Killing the editor instead of quitting it, test harnesses that dispose the connection abruptly, process supervisors recycling the server, or timeouts that hard-kill the language server.
Related errors
- Received exit notification before a shutdown request
- InvalidParams
- Failed to get the current working directory while creating a
- MethodNotFound
- InternalError
AI-assisted analysis of astral-sh/ruff@672bb4edf0 (2026-08-16).
Data as JSON: /api/errors/fbb133cd50a5d468.
Report an issue: GitHub.