astral-sh/ruff · warning
Received exit notification before a shutdown request
Error message
Received exit notification before a shutdown request
What it means
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.
Source
Thrown at crates/ty_server/src/server/main_loop.rs:68
&req.method
);
client.respond_err(
req.id,
lsp_server::ResponseError {
code: lsp_server::ErrorCode::InvalidRequest as i32,
message: "Shutdown already requested".to_owned(),
data: None,
},
);
continue;
}
api::request(req)
}
Message::Notification(notification) => {
if notification.method == lsp_types::ExitNotification::METHOD.as_str() {
if !self.session.is_shutdown_requested() {
return Err(anyhow!(
"Received exit notification before a shutdown request"
));
}
tracing::debug!("Received exit notification, exiting");
return Ok(());
}
api::notification(notification)
}
// Handle the response from the client to a server request
Message::Response(response) => {
if let Some(handler) = self
.session
.request_queue_mut()
.outgoing_mut()
.complete(&response.id)View on GitHub (pinned to 672bb4edf0)
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
Example fix
// before
client.notify('exit'); // exit before shutdown -> error
// after
await client.sendRequest('shutdown');
client.notify('exit'); Defensive patterns
Strategy: fallback
Validate before calling
// TS: enforce ordering with a small lifecycle helper
let shutdownAcked = false;
await client.sendRequest('shutdown');
shutdownAcked = true;
client.notify('exit'); Try / catch
// TS: if exit raced ahead (e.g. timeout path), restart cleanly rather than reusing state
try { await stopServer(client); }
catch (e) {
log.warn('server exited uncleanly; spawning a fresh instance');
await startServer();
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- client exited without proper shutdown sequence
- 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/0da3b6a6b569c6dc.
Report an issue: GitHub.