Hmbown/CodeWhale · error
LSP diagnostics channel closed before publishDiagnostics
Error message
LSP diagnostics channel closed before publishDiagnostics
What it means
The mpsc receiver on the diagnostics channel returned None, meaning all senders were dropped — the LSP server task (or the client internals that own the sender) has shut down before delivering publishDiagnostics. The library treats this as an error rather than an empty diagnostic result, because a closed channel is a shutdown signal, not 'no problems found'.
Solutions
- Check the language-server command/path starts correctly and the process stays alive (capture stderr logs)
- Guard against concurrent shutdown — don't call diagnostics_for after dropping the client or during shutdown
- Reinitialize/restart the LSP client and retry the request
- If this happens in tests, ensure the server stub actually emits publishDiagnostics before exiting
Example fix
// before
let diags = client.diagnostics_for(path, text, wait).await?; // Err: channel closed
// after
if !client.is_alive().await {
client.restart().await?;
}
let diags = client.diagnostics_for(path, text, wait).await?; Defensive patterns
Strategy: try-catch
Validate before calling
// check client/server health before waiting on diagnostics
if !client.is_alive().await {
anyhow::bail!("LSP server not running; restart before requesting diagnostics");
} Try / catch
match client.diagnostics_for(path, text, wait).await {
Ok(d) => d,
Err(e) if e.to_string().contains("channel closed") => {
client.restart().await?;
client.diagnostics_for(path, text, wait).await
}
Err(e) => return Err(e),
} Prevention
- Never call diagnostics_for after initiating client shutdown
- Validate the language-server command runs at all (config smoke test at startup)
- Capture child stderr to catch early server crashes
- Treat channel-closed as a restart signal, not empty diagnostics
When it happens
Trigger: The language-server child process exited or the reader task ended while diagnostics_for was waiting on rx.recv(); the client was shut down concurrently with a diagnostics request; server crashed mid-session.
Common situations: LSP binary path misconfigured so the server exits immediately; server crashed on a malformed document; shutdown/drop racing an in-flight diagnostics wait in tests or during editor close.
Related errors
- LSP outbound channel closed
- LSP request channel closed
- Compaction lifecycle task ended before acknowledgement
- duplicate LSP Content-Length
- engine channel closed
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/f24b9f37e641cdf4.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/lsp/client.rs:406
let path_buf = path.to_path_buf();
let (_, version) = timeout(
deadline.saturating_duration_since(tokio::time::Instant::now()),
self.open_or_change(path, text),
)
.await
.map_err(|_| anyhow!("LSP diagnostics timed out sending document"))??;
loop {
let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());
if remaining.is_zero() {
return Err(anyhow!(
"LSP diagnostics timed out before a current publication"
));
}
let mut rx = self.diagnostics_rx.lock().await;
let (file, published_version, items) = match timeout(remaining, rx.recv()).await {
Ok(Some(item)) => item,
Ok(None) => {
return Err(anyhow!(
"LSP diagnostics channel closed before publishDiagnostics"
));
}
Err(_) => {
return Err(anyhow!(
"LSP diagnostics timed out before a current publication"
));
}
};
if file != path_buf || published_version.is_some_and(|published| published != version) {
continue;
}
return Ok(DiagnosticPublication {
items,
document_version: Some(version),
diagnostic_version: published_version,
});
}View on GitHub (pinned to 73e0f67d83)