astral-sh/ruff · error · anyhow::Error

Failed to send response for request

Error message

Failed to send response for request {id}: {error}

What it means

Responses to client requests are queued onto an internal Event::SendResponse channel consumed by the main loop, which serializes them to the transport. If the main loop has already exited (client disconnected, transport failure, or shutdown race), sending the response fails and this error names the request ID whose answer could not be delivered.

Solutions

  1. Check earlier in the log for the underlying cause — the main loop exits for a reason (broken pipe, fatal error) and that is the real issue
  2. Restart the language server; a one-off occurrence during teardown is harmless
  3. If it happens mid-session, inspect the transport setup (stdout dedicated to LSP, logs on stderr) and client stability
  4. Update the server to pick up shutdown-ordering fixes
Defensive patterns

Strategy: try-catch

Try / catch

// Rust: respond best-effort; a closed channel means nobody is listening
if let Err(e) = client.respond(id, result) {
    tracing::warn!(%e, "could not deliver response; client gone or shutting down");
}

Prevention

When it happens

Trigger: A request handler finishing after the client vanished mid-request; transport write errors earlier in the log that terminated the main loop; shutdown racing an in-flight background request such as formatting.

Common situations: Editors killed while a slow format/hover request was outstanding; pipes closed prematurely by wrappers; supervisor-driven restarts where the old process logs send failures before dying.

Related errors


AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20). Data as JSON: /api/errors/bd6805b259eaf9cf. Report an issue: GitHub.

Appendix: source

Thrown at crates/ruff_server/src/session/client.rs:147

    /// and checked for cancellation (each request must have exactly one response).
    pub(crate) fn respond<R>(
        &self,
        id: &RequestId,
        result: crate::server::Result<R>,
    ) -> crate::Result<()>
    where
        R: serde::Serialize,
    {
        let response = match result {
            Ok(res) => lsp_server::Response::new_ok(id.clone(), res),
            Err(crate::server::Error { code, error }) => {
                lsp_server::Response::new_err(id.clone(), code as i32, error.to_string())
            }
        };

        self.main_loop_sender
            .send(Event::SendResponse(response))
            .map_err(|error| anyhow!("Failed to send response for request {id}: {error}"))
    }

    /// Sends an error response to the client for a given request ID.
    ///
    /// The response isn't sent immediately. Instead, it's queued up in the main loop.
    pub(crate) fn respond_err(
        &self,
        id: RequestId,
        error: lsp_server::ResponseError,
    ) -> crate::Result<()> {
        let response = lsp_server::Response {
            id,
            response_result: Err(error),
        };

        self.main_loop_sender
            .send(Event::SendResponse(response))
            .map_err(|error| anyhow!("Failed to send response: {error}"))

View on GitHub (pinned to d1087a4b9e)