{"record":{"id":"97141d6ff4a8ab21","repo":"Hmbown/CodeWhale","slug":"lsp-request-channel-closed","errorCode":null,"errorMessage":"LSP request channel closed","messagePattern":"LSP request channel closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/lsp/client.rs","lineNumber":329,"sourceCode":"            \"params\": params,\n        });\n        if let Err(err) = send_message(&self.tx_outbound, &payload).await {\n            let mut pending = self.pending.lock().await;\n            pending.remove(&id);\n            return Err(err);\n        }\n        match timeout(wait, rx).await {\n            Ok(Ok(reply)) => {\n                if let Some(error) = reply.get(\"error\") {\n                    let message = error\n                        .get(\"message\")\n                        .and_then(|v| v.as_str())\n                        .unwrap_or(\"LSP request failed\");\n                    return Err(anyhow!(\"{message}\"));\n                }\n                Ok(reply.get(\"result\").cloned().unwrap_or(Value::Null))\n            }\n            Ok(Err(_)) => Err(anyhow!(\"LSP request channel closed\")),\n            Err(_) => {\n                let mut pending = self.pending.lock().await;\n                pending.remove(&id);\n                Err(anyhow!(\"LSP request timed out for {method}\"))\n            }\n        }\n    }\n\n    async fn shutdown(&self) {\n        let mut child = self.child.lock().await;\n        if let Some(mut c) = child.take() {\n            let _ = c.start_kill();\n            let _ = c.wait().await;\n        }\n    }\n}\n\n/// Send a JSON value as one Content-Length-framed JSON-RPC message.","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/lsp/client.rs#L311-L347","documentation":"The reply channel for a pending request closed before an answer arrived, which happens when the LSP reader task exits - normally because the server process died or closed its stdout. The request can never complete; this error distinguishes a dead server from the separate timeout case.","triggerScenarios":"Server binary crashes mid-request (panic, OOM kill), server exiting after stdin EOF, a wrong server command that starts and immediately stops, or client teardown dropping the reader task while requests are in flight.","commonSituations":"Flaky language servers on large workspaces, nightly server builds crashing on specific files, system OOM killers, containers reaping background processes.","solutions":["Check whether the language server process is still alive; restart it (or reopen the session so the client respawns it)","Run the configured server command manually with the same arguments to see its crash output","Update the server version if the crash reproduces on specific files","Raise memory limits if the server is being OOM-killed"],"exampleFix":"// before: request into a dead server\nlet reply = client.request(\"textDocument/definition\", params).await; // Err(channel closed)\n// after: detect exit, respawn once, then retry\nif reply.is_err() && client.server_exited().await {\n    client = LspClient::spawn(server_cmd).await?;\n    reply = client.request(\"textDocument/definition\", params).await;\n}","handlingStrategy":"retry","validationCode":"// Health gate before issuing requests: has the child exited?\nfn server_alive(child: &mut tokio::process::Child) -> bool {\n    child.try_wait().map(|status| status.is_none()).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"// One bounded restart, then surface the failure\nfor attempt in 0..2 {\n    match client.request(method, params).await {\n        Ok(reply) => break Ok(reply),\n        Err(e) if e.to_string().contains(\"channel closed\") && attempt == 0 => {\n            client = restart_server().await?;\n        }\n        Err(e) => break Err(e),\n    }\n}","preventionTips":["Watch the server child process; mark the client dead on exit so requests fail fast","Log server stderr to a file for post-crash diagnosis","Pin known-good language server versions","Bound restarts to one attempt to avoid restart loops"],"tags":["lsp","process","channel","crash"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}