n0-computer/iroh · error · SendError

ExceedsMaxPacketSize

ExceedsMaxPacketSize

Error message

Packet exceeds max packet size

What it means

The relay server tried to send a RelayToClientMsg to a connected client whose encoded size exceeds MAX_PACKET_SIZE. Sending is aborted in start_send before the packet hits the wire, returning SendError::ExceedsMaxPacketSize with the offending size.

Solutions

  1. Split the message into multiple packets so each encoded packet stays within MAX_PACKET_SIZE.
  2. Reduce the number of datagrams batched into a single RelayToClientMsg::Datagrams.
  3. Check item.encoded_len() before enqueueing and chunk accordingly.
  4. Compress or shrink payloads before sending over the relay.

Example fix

// before
sink.start_send(big_msg).await?; // may exceed MAX_PACKET_SIZE
// after
for chunk in chunk_by_encoded_len(big_msg, MAX_PACKET_SIZE) {
    sink.start_send(chunk).await?;
}
Defensive patterns

Strategy: validation

Validate before calling

fn fits_in_packet(msg: &RelayToClientMsg) -> bool { msg.encoded_len() <= MAX_PACKET_SIZE }

Type guard

fn valid_packet(msg: RelayToClientMsg) -> Option<RelayToClientMsg> { (msg.encoded_len() <= MAX_PACKET_SIZE).then_some(msg) }

Try / catch

match sink.start_send(item) {
    Err(SendError::ExceedsMaxPacketSize { size }) => { chunk_and_resend(size); }
    other => other?,
}

Prevention

When it happens

Trigger: Producing a RelayToClientMsg::Datagrams (or other relay-to-client message) whose encoded length is greater than MAX_PACKET_SIZE, e.g. by batching too many/large datagrams into one packet, then passing it through the client send sink.

Common situations: Applications that accumulate many datagrams before flushing, large payload batching across a relay connection, misconfigured MTU/packet-size assumptions when porting code between direct and relayed connections.

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


AI-assisted analysis of n0-computer/iroh@2b4de030ce (2026-09-08). Data as JSON: /api/errors/ff68a6494a7616de. Report an issue: GitHub.

Appendix: source

Thrown at iroh-relay/src/server/streams.rs:132

    },
    /// Attempted to send an empty packet
    #[error("Attempted to send empty packet")]
    EmptyPacket {},
}

impl<S> Sink<RelayToClientMsg> for RelayedStream<S>
where
    S: Sink<bytes::Bytes, Error = StreamError> + Unpin,
{
    type Error = SendError;

    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Pin::new(&mut self.inner).poll_ready(cx).map_err(Into::into)
    }

    fn start_send(mut self: Pin<&mut Self>, item: RelayToClientMsg) -> Result<(), Self::Error> {
        let size = item.encoded_len();
        ensure!(
            size <= MAX_PACKET_SIZE,
            SendError::ExceedsMaxPacketSize { size }
        );
        if let RelayToClientMsg::Datagrams { datagrams, .. } = &item {
            ensure!(!datagrams.contents.is_empty(), SendError::EmptyPacket);
        }

        Pin::new(&mut self.inner)
            .start_send(item.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.inner).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.inner).poll_close(cx).map_err(Into::into)

View on GitHub (pinned to 2b4de030ce)