n0-computer/iroh · error · SendError
ExceedsMaxPacketSize
ExceedsMaxPacketSize
Error message
Exceeds max packet size ({MAX_PACKET_SIZE}): {size} What it means
This SendError variant is raised by the relay client connection's Sink::start_send when a ClientToRelayMsg serializes to more than MAX_PACKET_SIZE bytes. The relay protocol enforces a fixed per-packet size limit, so oversized frames are rejected client-side before being written to the connection. It guards against wasted bandwidth and relay-side rejection.
Solutions
- Check frame.encoded_len() against MAX_PACKET_SIZE before sending and split or truncate the payload into multiple datagram batches.
- Reduce per-datagram payload size so the total encoded frame stays under MAX_PACKET_SIZE.
- If large transfers are needed, move the data over a direct connection and use the relay only for hole-punching/control messages.
Example fix
// before
let msg = ClientToRelayMsg::Datagrams { .. }; // many datagrams batched
client.send(msg).await?;
// after
if msg.encoded_len() > MAX_PACKET_SIZE {
for chunk in split_into_batches(datagrams, MAX_PACKET_SIZE) {
client.send(ClientToRelayMsg::Datagrams { datagrams: chunk, .. }).await?;
}
} else {
client.send(msg).await?;
} Defensive patterns
Strategy: validation
Validate before calling
fn fits_in_packet(msg: &ClientToRelayMsg) -> bool {
msg.encoded_len() <= MAX_PACKET_SIZE
} Prevention
- Batch datagrams with a running encoded_len budget capped at MAX_PACKET_SIZE.
- Keep individual datagram payloads well below the limit to leave room for framing.
- Use direct connections for large transfers instead of relay datagrams.
When it happens
Trigger: Calling send on a RelayClient connection with a Datagrams message whose accumulated payload encodes to more than MAX_PACKET_SIZE bytes; batching too many/large datagrams into a single ClientToRelayMsg::Datagrams frame.
Common situations: Applications stuffing large payloads (e.g. big messages or file chunks) into relay datagrams, or batching code that keeps appending datagrams without checking cumulative encoded size before flushing.
Understand the failure class
Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.
Related errors
- EmptyPacket
- ExceedsMaxPacketSize
- 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/f05f2552e66d6c4a.
Report an issue: GitHub.
Appendix: source
Thrown at iroh-relay/src/client/conn.rs:149
RelayToClientMsg::from_bytes(msg, &self.key_cache, self.protocol_version);
Poll::Ready(Some(message.map_err(Into::into)))
}
Some(Err(e)) => Poll::Ready(Some(Err(anyerr!(e).into()))),
None => Poll::Ready(None),
}
}
}
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)