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

  1. Verify the proc-macro-srv binary rust-analyzer spawns exists and matches the toolchain.
  2. Inspect proc-macro-srv output/stderr for crashes or load failures of the target crate.
  3. Try the bidirectional protocol (proc-macro server setting) if available, which gives richer error context.
  4. 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

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


AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10). Data as JSON: /api/errors/695fd0ef8c759fbe. Report an issue: GitHub.