BoundaryML/baml · error
LSP client sent exit before completing shutdown
Error message
LSP client sent exit before completing shutdown
What it means
Raised in `run_stdio_loop` when the LSP client sends the `exit` notification before a `shutdown` request completed. Per the LSP lifecycle, `exit` after `shutdown` is clean; `exit` before completed shutdown is an abnormal termination, so the server bails with a nonzero exit for stdio.
Source
Thrown at baml_language/crates/baml_lsp_server/src/lib.rs:921
break;
}
SubmitResult::Closed => {
terminate = true;
break;
}
}
}
if terminate || stdio_closed.load(std::sync::atomic::Ordering::Acquire) {
break;
}
}
runtime.close_session(stdio_session);
if abnormal_exit {
// Lifecycle rule: `exit` before a completed shutdown is an abnormal
// termination (nonzero for stdio).
anyhow::bail!("LSP client sent exit before completing shutdown");
}
tracing::info!("LSP server shutting down");
Ok(())
}
fn absolutize_workspace_roots(workspace_roots: Vec<PathBuf>) -> anyhow::Result<Vec<PathBuf>> {
if workspace_roots.iter().all(|root| root.is_absolute()) {
return Ok(workspace_roots);
}
let cwd = std::env::current_dir().context("Failed to read current directory")?;
Ok(workspace_roots
.into_iter()
.map(|root| {
if root.is_absolute() {
root
} else {View on GitHub (pinned to bd85ce9dee)
Solutions
- Fix the client to send `shutdown` (request) and wait for its response before sending `exit`
- If you control the editor plugin, implement the full LSP lifecycle: initialize → shutdown → exit
- Check the client's shutdown timeout settings and increase if it aborts mid-shutdown
- Inspect server logs to confirm `shutdown` was received before `exit`
Example fix
// before (client): fire-and-forget exit
connection.exit();
// after (client): complete the lifecycle handshake
let _ = connection.request("shutdown", null).await;
connection.notify("exit"); Defensive patterns
Strategy: try-catch
Try / catch
// client side: only send exit after shutdown resolved
match shutdown_result {
Ok(_) => connection.notify("exit"),
Err(e) => log::error!("shutdown failed, skipping exit: {e}"),
} Prevention
- Implement the full LSP lifecycle: initialize → shutdown (await response) → exit
- Disable hard process kills on editor close in favor of the handshake
- Increase client shutdown timeouts
When it happens
Trigger: Client sends the `exit` notification while a `shutdown` request is still pending or was never sent; a client crash or hard kill issuing `exit` mid-shutdown; a client bug skipping the `shutdown` request.
Common situations: Editors killing the language server process on close instead of performing the full shutdown/exit handshake; flaky LSP client plugins that fire `exit` immediately; timeouts in the client causing it to abandon shutdown.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- {0}
- Notification not supported: {0}
- Request not supported: {0}
- Failed to serialize request result: {0}
- no filesystem is attached to this server ({})
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/61edd6baaacb08a6.
Report an issue: GitHub.