valeriansaliou/sonic · error · std::io::Error
BrokenPipe
Error message
BrokenPipe
What it means
Channel::send maps a failure on the internal oneshot/command channel (timing out after SEND_TIMEOUT or the receiver dropping) into an io::Error of kind BrokenPipe. It means the command could not be delivered to the channel worker — the connection is effectively dead.
Source
Thrown at client/src/channel.rs:156
if let Err(error) = send_res {
// Only log an error, as this would happen if the receiver is dropped.
log_error!("Could not send response: {error}");
}
}
Err(error) => {
if let Err(send_error) = tx.send(Err(error)) {
// Only log an error, as this would happen if the receiver is dropped.
log_error!("Could not send response: {send_error}");
}
}
}
}),
},
SEND_TIMEOUT,
)
.map_err(|error| {
std::io::Error::new(std::io::ErrorKind::BrokenPipe, error.to_string())
})?;
self.poll_waker.wake()?;
Ok(rx)
}
/// Sends a command asynchronous at the protocol level (e.g. using the
/// `PENDING` + `EVENT` pattern).
pub(crate) fn send_async<T: Send + 'static>(
&self,
command: Command,
discriminant1: impl Into<Mode::Discriminant>,
make_discriminant2: impl FnOnce(&str) -> Mode::Discriminant + Send + Sync + 'static,
parse: impl Fn(&str) -> std::io::Result<T> + Send + 'static,
) -> std::io::Result<oneshot::Receiver<std::io::Result<T>>> {
if command.len() > self.channel_info.buffer_size {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,View on GitHub (pinned to e6a72da6a5)
Solutions
- Reconnect the channel/client and retry the command
- Check server logs for why the channel thread closed (e.g. buffer overflow, protocol error)
- Verify the server is still running and reachable (health check)
- Reduce command cost or increase tolerance for slow commands so SEND_TIMEOUT is not hit
Example fix
// client
// before
let rx = channel.send(cmd, disc, parse)?; // panics/errs with BrokenPipe if connection died
// after
match channel.send(cmd, disc, parse) {
Ok(rx) => ...,
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => reconnect_and_retry(),
Err(e) => return Err(e),
} Defensive patterns
Strategy: retry
Validate before calling
// detect a dead channel before sending
if channel.is_closed() || last_send_failed {
reconnect();
} Try / catch
match channel.send(cmd, disc, parse) {
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
reconnect();
channel.send(cmd, disc, parse)?;
}
other => other?,
} Prevention
- Wrap send with reconnect-and-retry logic
- Ping periodically to detect dead connections early
- Keep commands short enough to finish within SEND_TIMEOUT
- Watch for server-side channel panics that kill the worker
When it happens
Trigger: send_buffered -> send: the oneshot::Sender::send + timeout(SEND_TIMEOUT) path returns Err (worker stopped, receiver dropped, or timeout elapsed), which is converted via map_err to ErrorKind::BrokenPipe.
Common situations: Server closed or reset the connection (see the server's 'closing channel' panic); long-blocking commands exceeding SEND_TIMEOUT; using a Channel object after the underlying connection was dropped.
Related errors
- Command too long. Max buffer size: {}
- buffer overflow ({}/{} bytes)
- closing channel
- BrokenPipe
- InvalidData
AI-assisted analysis of valeriansaliou/sonic@e6a72da6a5 (2026-09-01).
Data as JSON: /api/errors/9e39bd2899135a0c.
Report an issue: GitHub.