{"record":{"id":"c39dfa671c11f5d3","repo":"Kuberwastaken/claurst","slug":"lsp-server-closed-stdout","errorCode":null,"errorMessage":"LSP server closed stdout","messagePattern":"LSP server closed stdout","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-rust/crates/core/src/lsp.rs","lineNumber":145,"sourceCode":"    writer: &mut BufWriter<ChildStdin>,\r\n    body: &str,\r\n) -> anyhow::Result<()> {\r\n    let header = format!(\"Content-Length: {}\\r\\n\\r\\n\", body.len());\r\n    writer.write_all(header.as_bytes()).await?;\r\n    writer.write_all(body.as_bytes()).await?;\r\n    writer.flush().await?;\r\n    Ok(())\r\n}\r\n\r\nasync fn read_message(\r\n    reader: &mut BufReader<ChildStdout>,\r\n) -> anyhow::Result<serde_json::Value> {\r\n    let mut content_length: usize = 0;\r\n    loop {\r\n        let mut line = String::new();\r\n        let n = reader.read_line(&mut line).await?;\r\n        if n == 0 {\r\n            return Err(anyhow::anyhow!(\"LSP server closed stdout\"));\r\n        }\r\n        let trimmed = line.trim_end_matches(['\\r', '\\n']);\r\n        if trimmed.is_empty() {\r\n            break;\r\n        }\r\n        if let Some(val) = trimmed.strip_prefix(\"Content-Length: \") {\r\n            content_length = val.trim().parse()?;\r\n        }\r\n    }\r\n    if content_length == 0 {\r\n        return Err(anyhow::anyhow!(\"LSP message missing Content-Length header\"));\r\n    }\r\n    let mut buf = vec![0u8; content_length];\r\n    reader.read_exact(&mut buf).await?;\r\n    Ok(serde_json::from_slice(&buf)?)\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/Kuberwastaken/claurst/blob/b0637c97ec34144387cbf2f74f65df6d16a6cef1/src-rust/crates/core/src/lsp.rs#L127-L163","documentation":"The LSP client's framing reader (`read_message`, driven by `start`) does a blocking `read_line` on the language-server's stdout and treats a zero-byte read as end-of-stream. Reaching EOF means the LSP server process terminated (or closed its stdout) mid-protocol, so no message can be read and the client aborts with this error.","triggerScenarios":"Any `read_message` call after the language server's stdout hits EOF: the server process crashed, exited due to a startup failure (bad executable, missing workspace root, unsupported flags), or was killed externally while the client awaited a response.","commonSituations":"The configured LSP binary path is wrong or the binary exits immediately; the server panics on a malformed request; the machine runs out of memory and the OOM killer reaps the server; a shell wrapper script exits instead of exec-ing the server.","solutions":["Run the language-server command from the error/config manually in a terminal to see why it exits (bad path, missing args, immediate crash).","Check the server's stderr output — LSP servers log startup errors there; fix the underlying startup problem (binary path, version, workspace root).","Verify the server process stays alive (`ps aux | grep <server>`) while the client is running; if it dies, capture its exit code/stderr.","Update or reinstall the language server if it crashes on the current project."],"exampleFix":"// before: server dies, stdout EOF\n\"rust-analyzer\": { \"command\": \"rust-analyser\" }  // typo, exec fails and process exits\n\n// after\n\"rust-analyzer\": { \"command\": \"rust-analyzer\" }","handlingStrategy":"try-catch","validationCode":"// before starting the LSP session, confirm the server binary exists and runs:\nlet status = std::process::Command::new(&server_cmd)\n    .arg(\"--version\")\n    .status()\n    .map_err(|e| anyhow::anyhow!(\"LSP server `{}` not runnable: {e}\", server_cmd.display()))?;\nif !status.success() {\n    anyhow::bail!(\"LSP server `--version` exited with {status}\");\n}","typeGuard":"fn server_binary_available(cmd: &str) -> bool {\n    std::process::Command::new(cmd)\n        .arg(\"--version\")\n        .output()\n        .map(|o| o.status.success())\n        .unwrap_or(false)\n}","tryCatchPattern":"match lsp.start().await {\n    Err(e) if e.to_string() == \"LSP server closed stdout\" => {\n        eprintln!(\"Language server died before responding. Check its stderr and binary path: {e:#}\");\n        // inspect server stderr / restart server\n    }\n    other => other?,\n}","preventionTips":["Verify the LSP binary path resolves (`which <server>`) before configuring it.","Keep language servers updated; old versions may crash on newer project files.","Watch server stderr — most crashes are logged there before stdout closes.","Test the server standalone with `--version` or a minimal initialize request before wiring it into the client.","Monitor resource limits (OOM kills are a common cause of silent server death)."],"tags":["lsp","stdio","process-crash","ipc"],"backgroundTag":"broken-pipe","analyzedSha":"b0637c97ec34144387cbf2f74f65df6d16a6cef1","analyzedAt":"2026-09-10T00:24:58.650Z","contentChangedAt":"2026-09-10T00:24:58.650Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}