rust-lang/rust · warning · anyhow::Error
client exited without proper shutdown sequence
Error message
client exited without proper shutdown sequence
What it means
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.
Source
Thrown at src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs:205
.map(|f| format!("**/{f}"));
self.register_did_save_capability(additional_patterns);
}
if self.config.discover_workspace_config().is_none() {
self.fetch_workspaces_queue.request_op(
"startup".to_owned(),
FetchWorkspaceRequest { path: None, force_crate_graph_reload: false },
);
if let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) =
self.fetch_workspaces_queue.should_start_op()
{
self.fetch_workspaces(cause, path, force_crate_graph_reload);
}
}
while let Ok(event) = self.next_event(&inbox) {
let Some(event) = event else {
anyhow::bail!("client exited without proper shutdown sequence");
};
if matches!(
&event,
Event::Lsp(lsp_server::Message::Notification(Notification { method, .. }))
if method == lsp_types::ExitNotification::METHOD.as_str()
) {
return Ok(());
}
self.handle_event(event);
}
Err(anyhow::anyhow!("A receiver has been dropped, something panicked!"))
}
fn register_did_save_capability(&mut self, additional_patterns: impl Iterator<Item = String>) {
let additional_filters = additional_patterns.map(|pattern| {
lsp_types::DocumentFilter::TextDocumentFilter(lsp_types::TextDocumentFilter::Pattern(
lsp_types::TextDocumentFilterPattern {View on GitHub (pinned to 7088e4b63a)
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.
Example fix
// before: bail on a clean EOF
let Some(event) = event else {
anyhow::bail!("client exited without proper shutdown sequence");
};
// after: log and exit cleanly (a missing exit is the client's fault, not fatal for r-a)
let Some(event) = event else {
tracing::warn!("client disconnected without sending `exit`; shutting down");
return Ok(());
}; Defensive patterns
Strategy: try-catch
Validate before calling
// Client-side: always perform the LSP shutdown handshake before disconnecting:
async fn shutdown_graceful(server: &LspClient) -> Result<(), Box<dyn std::error::Error>> {
let _ = server.request("shutdown").await?;
server.notify("exit").await?;
server.close().await?;
Ok(())
} Type guard
null
Try / catch
// Server-side: treat a clean EOF as a normal exit rather than an error:
let Some(event) = event else {
tracing::warn!("client disconnected without `exit`; treating as shutdown");
return Ok(());
}; Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid LSP resolve data
- A receiver has been dropped, something panicked!
- rust-analyzer Language Server is not available. Please, ensu
- Failed to execute ${path} --version.
- bootstrap error. See the logs in "OUTPUT > Rust Analyzer Cli
AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10).
Data as JSON: /api/errors/354000bebf9d735b.
Report an issue: GitHub.