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

  1. Check the language-server command/path starts correctly and the process stays alive (capture stderr logs)
  2. Guard against concurrent shutdown — don't call diagnostics_for after dropping the client or during shutdown
  3. Reinitialize/restart the LSP client and retry the request
  4. 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

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


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)