{"record":{"id":"09b57034aea5d5d7","repo":"rust-lang/rust-analyzer","slug":"proc-macro-server-closed-the-stream","errorCode":null,"errorMessage":"proc-macro server closed the stream","messagePattern":"proc-macro server closed the stream","errorType":"error_code","errorClass":"ServerError","httpStatus":null,"severity":"error","filePath":"crates/proc-macro-api/src/bidirectional_protocol.rs","lineNumber":48,"sourceCode":"\npub type SubCallback<'a> = &'a dyn Fn(SubRequest) -> Result<SubResponse, ServerError>;\n\npub fn run_conversation(\n    writer: &mut dyn Write,\n    reader: &mut dyn BufRead,\n    buf: &mut Vec<u8>,\n    msg: BidirectionalMessage,\n    callback: SubCallback<'_>,\n) -> Result<BidirectionalMessage, ServerError> {\n    let encoded = postcard::encode(&msg).map_err(wrap_encode)?;\n    postcard::write(writer, &encoded).map_err(wrap_io(\"failed to write initial request\"))?;\n\n    loop {\n        let maybe_buf = postcard::read(reader, buf).map_err(wrap_io(\"failed to read message\"))?;\n        let Some(b) = maybe_buf else {\n            return Err(ServerError {\n                message: \"proc-macro server closed the stream\".into(),\n                io: Some(Arc::new(io::Error::new(io::ErrorKind::UnexpectedEof, \"closed\"))),\n            });\n        };\n\n        let msg: BidirectionalMessage = postcard::decode(b).map_err(wrap_decode)?;\n\n        match msg {\n            BidirectionalMessage::Response(response) => {\n                return Ok(BidirectionalMessage::Response(response));\n            }\n            BidirectionalMessage::SubRequest(sr) => {\n                // TODO: Avoid `AssertUnwindSafe` by making the callback `UnwindSafe` once `SourceDatabase`\n                // becomes unwind-safe (currently blocked by `parking_lot::RwLock` in the VFS).\n                let resp = match catch_unwind(AssertUnwindSafe(|| callback(sr))) {\n                    Ok(Ok(resp)) => BidirectionalMessage::SubResponse(resp),\n                    Ok(Err(err)) => BidirectionalMessage::SubResponse(SubResponse::Cancel {\n                        reason: err.to_string(),\n                    }),\n                    Err(_) => BidirectionalMessage::SubResponse(SubResponse::Cancel {","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/crates/proc-macro-api/src/bidirectional_protocol.rs#L30-L66","documentation":"`ServerError` with message `proc-macro server closed the stream` is produced by the bidirectional protocol's read loop when `postcard::read` returns EOF (`None`) from the proc-macro server's stdout stream: the server process stopped sending messages without a proper shutdown. It carries an `UnexpectedEof` io error as its cause.","triggerScenarios":"The proc-macro server process exits or its stdout closes mid-session (crash, being killed, dylib load failure, or an ABI/version mismatch making the server bail immediately after start).","commonSituations":"Outdated proc-macro dylibs built with a different rustc version than the server; proc-macro server crashing on a malformed/expanding macro; sandbox/AV software killing the helper process; stale `target/` artifacts after a toolchain update.","solutions":["Run `cargo clean` (or delete `target/`) so proc-macro dylibs are rebuilt for the current toolchain, then restart rust-analyzer.","Update rust-analyzer and rebuild workspace dependencies so server and dylib ABI match.","Reproduce the proc-macro crash with `cargo build` / `cargo expand` to find the offending macro crate and fix or pin it.","Check for the server process being killed (OOM, antivirus, sandbox) in system logs.","If it persists, disable proc-macro support (`rust-analyzer.procMacro.enable = false`) to keep other features working."],"exampleFix":"// before\n# stale dylib in target/\nrust-analyzer: proc-macro server started from old .so\n\n// after\n$ cargo clean\n$ # restart rust-analyzer so dylibs are rebuilt","handlingStrategy":"retry","validationCode":"// before starting a proc-macro server, verify the dylibs match the current toolchain\nfn dylibs_fresh(server_rustc: &str, workspace_dir: &Path) -> bool {\n    let fingerprint = workspace_dir.join(\"target/.rustc_info.json\");\n    fingerprint.exists() && std::fs::read_to_string(fingerprint).map_or(false, |s| s.contains(server_rustc))\n}","typeGuard":"fn is_eof_server_error(e: &ServerError) -> bool {\n    e.message.contains(\"closed the stream\")\n        && e.io.as_ref().map_or(false, |io| io.kind() == std::io::ErrorKind::UnexpectedEof)\n}","tryCatchPattern":"match server.read_message(&mut buf) {\n    Err(e) if is_eof_server_error(&e) => {\n        tracing::warn!(\"proc-macro server died: {e}\");\n        clean_target_dir_and_restart_server()?; // cargo clean, respawn process, retry once\n    }\n    other => other,\n}","preventionTips":["`cargo clean` after every toolchain update so dylib ABI matches the server.","Keep rust-analyzer and the workspace toolchain versions aligned.","Watch for the proc-macro server process dying in system logs (OOM/AV kills).","As a stopgap, disable proc-macro support in rust-analyzer settings."],"tags":["proc-macro","ipc","eof","rust-analyzer"],"backgroundTag":"proc-macro-server-crashed","analyzedSha":"e8f7e90aa3e7b26aa9a000200f606c1078da99ec","analyzedAt":"2026-09-03T21:08:06.959Z","contentChangedAt":"2026-09-03T21:08:06.959Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}