rust-lang/rust-analyzer · error
client exited without proper shutdown sequence
Error message
client exited without proper shutdown sequence
What it means
rust-analyzer's main loop reads events from the LSP connection. When the event channel closes (client's stdio/socket ends) without the client first sending an `exit` notification after `shutdown`, the loop treats this as a protocol violation and aborts with this error instead of exiting cleanly. It enforces the LSP lifecycle: clients must not just drop the connection.
Source
Thrown at 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 e8f7e90aa3)
Solutions
- Fix the client/editor integration to send a `shutdown` request followed by an `exit` notification before closing stdio
- Check for client crashes: if the editor was killed, this error is expected fallout of the abrupt termination and can be ignored in logs
- If you manage rust-analyzer programmatically, terminate gracefully by writing a `shutdown`/`exit` pair, then close stdin
- Update rust-analyzer/client versions to match, since protocol mismatches between old clients and new servers can break the lifecycle
Example fix
// client-side, before closing the process
// before
child.stdin.close();
// after
send_request("shutdown", None);
send_notification("exit", None);
child.stdin.close(); Defensive patterns
Strategy: try-catch
Try / catch
// client side: treat this as expected on abrupt kill
match server.wait() {
Err(e) if e.to_string().contains("without proper shutdown sequence") => {
// client was killed uncleanly; log at info level, not error
}
other => other?,
} Prevention
- Always send shutdown request then exit notification before closing the transport
- Never SIGKILL the server in tooling; prefer graceful LSP shutdown
- Keep client and rust-analyzer versions in sync
When it happens
Trigger: The client (editor) closes its end of the connection (closes stdin or kills the process pipe) while `run` is waiting on `next_event`, and no `shutdown` request + `exit` notification sequence was completed beforehand, so `next_event` returns `None`.
Common situations: Editor crashes or is force-killed; a wrapper script kills the server with SIGKILL instead of issuing an LSP shutdown; pipes are broken by the parent process exiting; misconfigured clients that terminate the transport abruptly.
Related errors
- {loop_e} {join_e}
- {loop_e}
- No file available to rename
- inconsistent text range
- No references found at position
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/64191cc27b43f324.
Report an issue: GitHub.