n0-computer/iroh · error · SendError
EmptyPacket
EmptyPacket
Error message
Attempted to send empty packet
What it means
SendError::EmptyPacket is raised in start_send when a ClientToRelayMsg::Datagrams frame carries an empty datagram list. Sending an empty packet would waste a round trip and violates the relay protocol, so it is rejected before encoding. Note the size check happens first, so an empty batch must still be non-empty to pass.
Solutions
- Skip the send entirely when datagrams.contents.is_empty() before constructing/sending the frame.
- Only build a Datagrams batch once at least one datagram has been queued.
- Audit flush logic so empty batches are dropped instead of sent.
Example fix
// before
let msg = ClientToRelayMsg::Datagrams { datagrams, .. };
client.send(msg).await?;
// after
if !datagrams.contents.is_empty() {
client.send(ClientToRelayMsg::Datagrams { datagrams, .. }).await?;
} Defensive patterns
Strategy: validation
Validate before calling
if datagrams.contents.is_empty() {
return; // skip send
} Prevention
- Only construct Datagrams batches when at least one datagram is queued.
- Make batch-assembly helpers return Option and skip sending on None.
- In flush loops, check for an empty queue before building a frame.
When it happens
Trigger: Sending a Datagrams message after its contents were fully drained/flushed, or constructing ClientToRelayMsg::Datagrams with datagrams.contents == vec![] and sending it on the connection.
Common situations: Queue-based senders that pop datagrams into a batch, then flush an empty batch when the queue was momentarily empty; race between batch assembly and connection flush.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- ExceedsMaxPacketSize
- EmptyPacket
- The relay is rate-limiting this endpoint; outbound relay…
- Invalid transport configuration
- UnexpectedUpgradeStatus
AI-assisted analysis of n0-computer/iroh@2b4de030ce (2026-09-08).
Data as JSON: /api/errors/a8ba57576ac9c7f5.
Report an issue: GitHub.
Appendix: source
Thrown at iroh-relay/src/client/conn.rs:154
}
}
}
impl Sink<ClientToRelayMsg> for Conn {
type Error = SendError;
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Pin::new(&mut self.conn).poll_ready(cx).map_err(Into::into)
}
fn start_send(mut self: Pin<&mut Self>, frame: ClientToRelayMsg) -> Result<(), Self::Error> {
let size = frame.encoded_len();
ensure!(
size <= MAX_PACKET_SIZE,
SendError::ExceedsMaxPacketSize { size }
);
if let ClientToRelayMsg::Datagrams { datagrams, .. } = &frame {
ensure!(!datagrams.contents.is_empty(), SendError::EmptyPacket);
}
Pin::new(&mut self.conn)
.start_send(frame.to_bytes().freeze())
.map_err(Into::into)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Pin::new(&mut self.conn).poll_flush(cx).map_err(Into::into)
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Pin::new(&mut self.conn).poll_close(cx).map_err(Into::into)
}
}
View on GitHub (pinned to 2b4de030ce)