Hmbown/CodeWhale · error
LSP request channel closed
Error message
LSP request channel closed
What it means
request registers a oneshot reply channel in the pending map, writes the JSON-RPC request to the server's stdin, and awaits the reply. This error means the reply channel's sender was dropped before a response arrived — the server-side reader/dispatch task ended (or the client is shutting down) while this request was in flight.
Solutions
- Verify the language-server process is alive (check exit status / stderr) and restart the client if it died
- Avoid issuing requests while the client is shutting down; check a healthy/ready flag first
- Capture server stderr logs to find why the reader task ended
- Retry the request after reinitialization
Example fix
// before
let reply = client.request("textDocument/hover", params, wait).await?;
// after
if client.is_shutdown() {
anyhow::bail!("LSP client shut down; reinitialize before requesting");
}
let reply = client.request("textDocument/hover", params, wait).await?; Defensive patterns
Strategy: try-catch
Validate before calling
// ensure the server process is alive and initialized before requesting
if !client.is_alive().await || !client.is_initialized().await {
anyhow::bail!("LSP server not ready; skipping request");
} Try / catch
match client.request(method, params, wait).await {
Ok(reply) => reply,
Err(e) if e.to_string() == "LSP request channel closed" => {
// server died mid-request: log, restart, optionally retry once
client.restart().await?;
client.request(method, params, wait).await
}
Err(e) => return Err(e),
} Prevention
- Track child exit status so a dead server is detected before requests
- Don't fire requests during client shutdown windows
- Capture server stderr/exit codes to explain reader-task termination
- Wrap request bursts in a liveness check
When it happens
Trigger: Language-server process exited so the reader task that resolves pending ids terminated; client shutdown dropped all pending senders; the reader task panicked or was aborted while requests were outstanding.
Common situations: LSP binary crashed mid-session (OOM, panic in server); server killed by shutdown while a hover/completion request was pending; misconfigured command that dies after start.
Related errors
- LSP diagnostics channel closed before publishDiagnostics
- LSP outbound channel closed
- LSP request timed out for
- LSP response has no result
- {message}
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/97141d6ff4a8ab21.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/lsp/client.rs:485
let id = *next;
*next = next.saturating_add(1);
id
};
let (tx, rx) = oneshot::channel();
{
let mut pending = self.pending.lock().await;
pending.insert(id, tx);
}
let payload = json!({
"jsonrpc": "2.0",
"id": id,
"method": method,
"params": params,
});
// The deadline includes queue backpressure, not just the reply.
let response = timeout(wait, async {
send_message(&self.tx_outbound, &payload).await?;
rx.await.map_err(|_| anyhow!("LSP request channel closed"))
})
.await;
self.pending.lock().await.remove(&id);
match response {
Ok(Ok(reply)) => {
if let Some(error) = reply.get("error") {
let message = error
.get("message")
.and_then(Value::as_str)
.unwrap_or("LSP request failed");
return Err(anyhow!("{message}"));
}
reply
.get("result")
.cloned()
.ok_or_else(|| anyhow!("LSP response has no result"))
}
Ok(Err(error)) => Err(error),View on GitHub (pinned to 73e0f67d83)