rust-lang/rust · error · ServerError
proc-macro server did not respond with data
Error message
proc-macro server did not respond with data
What it means
Returned by rust-analyzer's legacy (JSON) proc-macro IPC path send_task_legacy when the server responds with None instead of a Response. This means the proc-macro server accepted the request but produced no data - the wrapper synthesizes a BrokenPipe io::Error and the ServerError 'proc-macro server did not respond with data'. with_locked_io later upgrades this to the server's exit error if the process has terminated.
Source
Thrown at src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs:332
result
}
pub(crate) fn send_task_legacy<Request, Response>(
&self,
send: impl FnOnce(
&mut dyn Write,
&mut dyn BufRead,
Request,
&mut String,
) -> Result<Option<Response>, ServerError>,
req: Request,
) -> Result<Response, ServerError> {
self.with_locked_io(String::new(), |writer, reader, buf| {
send(writer, reader, req, buf).and_then(|res| {
res.ok_or_else(|| {
let message = "proc-macro server did not respond with data".to_owned();
ServerError {
io: Some(Arc::new(io::Error::new(
io::ErrorKind::BrokenPipe,
message.clone(),
))),
message,
}
})
})
})
}
fn with_locked_io<R, B>(
&self,
mut buf: B,
f: impl FnOnce(&mut dyn Write, &mut dyn BufRead, &mut B) -> Result<R, ServerError>,
) -> Result<R, ServerError> {
let state = &mut *self.state.lock().unwrap();
f(&mut state.stdin, &mut state.stdout, &mut buf).map_err(|e| {
if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) {View on GitHub (pinned to 7088e4b63a)
Solutions
- Verify the proc-macro-srv binary rust-analyzer spawns exists and matches the toolchain.
- Inspect proc-macro-srv output/stderr for crashes or load failures of the target crate.
- Try the bidirectional protocol (proc-macro server setting) if available, which gives richer error context.
- Disable proc-macro expansion for the problematic crate to restore IDE function.
Example fix
// runtime/IDE configuration fix, not source code. // In rust-analyzer settings, point to the correct server: // "procMacro.server": "/path/to/proc-macro-srv" // or build it from the same source tree: cargo build -p proc-macro-srv-cli
Defensive patterns
Strategy: try-catch
Validate before calling
// Before sending, confirm the server process is still running:
// if server.child.try_wait()?.is_some() { return Err("server gone"); } Try / catch
match send_task_legacy(send, req) {
Ok(r) => Ok(r),
Err(ServerError { io: Some(ref i), .. })
if i.kind() == io::ErrorKind::BrokenPipe => {
// server exited; surface exit_err, disable expansion
Err("proc-macro server exited without data".into())
}
Err(e) => Err(e),
} Prevention
- Use the bidirectional protocol when available for richer errors.
- Point rust-analyzer at a proc-macro-srv built from the same source.
- Disable proc-macro expansion for crates that crash the server.
When it happens
Trigger: A proc-macro expansion request over the legacy JSON protocol where the server closes stdout or returns an empty/partial frame - e.g. proc-macro-srv crashed, was killed, hit a fatal error, or the protocol got out of sync. Triggered when rust-analyzer has not negotiated the bidirectional protocol.
Common situations: proc-macro-srv binary missing or wrong version; proc-macro crate failing to compile/load inside the server; antivirus terminating the child; large/hung macro exhausting the server.
Related errors
- closed
- proc-macro-srv-cli needs to be compiled with the `in-rust-tr
- File path is invalid: {path}
- rust-analyzer Language Server is not available. Please, ensu
- Failed to execute ${path} --version.
AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10).
Data as JSON: /api/errors/695fd0ef8c759fbe.
Report an issue: GitHub.