zed-industries/zed · error

failed to send message: {error}

Error message

failed to send message: {error}

What it means

request_dynamic registers a response channel, then sends the envelope either buffered (while disconnected) or unbuffered. If the underlying send fails - the outgoing channel was closed because the connection handler shut down (crates/remote/src/remote_client.rs:1964) - it logs 'failed to send message' and bails with the transport error.

Source

Thrown at crates/remote/src/remote_client.rs:1985

    fn send<T: EnvelopedMessage>(&self, payload: T) -> Result<()> {
        log::debug!("remote send name:{}", T::NAME);
        self.send_dynamic(payload.into_envelope(0, None, None))
    }

    fn request_dynamic(
        &self,
        mut envelope: proto::Envelope,
        type_name: &'static str,
        use_buffer: bool,
    ) -> impl 'static + Future<Output = Result<proto::Envelope>> {
        envelope.id = self.next_message_id.fetch_add(1, SeqCst);
        let (tx, rx) = oneshot::channel();
        let mut response_channels_lock = self.response_channels.lock();
        response_channels_lock.insert(MessageId(envelope.id), tx);
        drop(response_channels_lock);

        let result = if use_buffer {
            self.send_buffered(envelope)
        } else {
            self.send_unbuffered(envelope)
        };
        async move {
            if let Err(error) = &result {
                log::error!("failed to send message: {error}");
                anyhow::bail!("failed to send message: {error}");
            }

            let response = rx.await.context("connection lost")?.0;
            if let Some(proto::envelope::Payload::Error(error)) = &response.payload {
                return Err(RpcError::from_proto(error, type_name));
            }
            Ok(response)
        }
    }

    fn request_stream_dynamic(

View on GitHub (pinned to 5a9b9558db)

Solutions

  1. Retry the request after the client reconnects (reconnect replays buffered envelopes where applicable)
  2. Check the connection state before issuing non-idempotent RPCs
  3. If sends fail persistently while supposedly connected, restart the remote server on the host
Defensive patterns

Strategy: retry

Validate before calling

// Gate RPCs on a live connection before issuing them
if !client.is_connected() {
    client.wait_until_connected(cx).await; // or trigger/await reconnect
}
client.request(proto::Ping {}).await?;

Try / catch

match client.request(payload.clone()).await {
    Err(e) if e.to_string().contains("failed to send message") => {
        // connection dropped mid-send: wait for reconnect, then re-issue once
        client.wait_until_connected(cx).await;
        client.request(payload).await
    }
    other => other,
}

Prevention

When it happens

Trigger: Issuing any RPC request() at the moment the connection drops: the multiplex/connection task has exited and the outgoing sink is closed, so send_buffered() or send_unbuffered() returns Err.

Common situations: Network blip between send setup and flush; remote server process died; requests racing the disconnect/reconnect transition.

Related errors


AI-assisted analysis of zed-industries/zed@5a9b9558db (2026-08-20). Data as JSON: /api/errors/ef5bff0123a83b34. Report an issue: GitHub.