n0-computer/iroh · error · Error
UnexpectedFrameType
UnexpectedFrameType
Error message
Unexpected tag, got {frame_type:?}, but expected one of {expected_types:?} What it means
Error::UnexpectedFrameType is raised by read_frame when the frame tag read from the wire is not among the expected_types the caller passed. Both client-side and server-side handshake readers use this to enforce which frame types are valid at a given protocol stage. It usually indicates protocol desync, corruption, or talking to a non-iroh/incompatible peer.
Solutions
- Ensure client and relay use compatible protocol versions (same frame-type encoding).
- Check that no proxy/middlebox is corrupting or buffering the websocket stream.
- Confirm the peer is actually an iroh relay speaking the expected protocol on that port.
- Log the received frame_type versus expected_types to identify the desync point.
Defensive patterns
Strategy: try-catch
Try / catch
// Rust
match read_frame(&mut stream, &[FrameType::Ping, FrameType::Pong]).await {
Err(err @ Error::UnexpectedFrameType { frame_type, expected_types }) => {
tracing::error!(?frame_type, ?expected_types, "protocol desync — reconnecting");
reconnect().await;
}
other => other?,
} Prevention
- Keep client and relay protocol versions aligned.
- Never write application frames before the handshake completes.
- Log received frame_type vs expected on every mismatch to spot desync early.
- Confirm the peer on that port is actually an iroh relay.
When it happens
Trigger: Handshake read_frame called with expected frame types (e.g. [Ping, Pong]) but the stream delivers another frame type, such as the client sending data before handshake completion or receiving a server frame out of order.
Common situations: Version-mismatched client and relay speaking different frame layouts; corrupted or interleaved streams; websocket framing gone wrong through a proxy; buggy custom code writing frames out of order.
Related errors
AI-assisted analysis of n0-computer/iroh@2b4de030ce (2026-09-08).
Data as JSON: /api/errors/4c0cc301c9e4f794.
Report an issue: GitHub.
Appendix: source
Thrown at iroh-relay/src/protos/handshake.rs:586
io.flush()
.await
.map_err(|err| e!(Error::Websocket, anyerr!(err)))?;
Ok(())
}
async fn read_frame(
io: &mut impl BytesStreamSink,
expected_types: &[FrameType],
) -> Result<(FrameType, Bytes), Error> {
let mut payload = io
.try_next()
.await
.map_err(|err| e!(Error::Websocket, anyerr!(err)))?
.ok_or_else(|| e!(Error::UnexpectedEnd))?;
let frame_type = FrameType::from_bytes(&mut payload)?;
trace!(?frame_type, "Reading frame");
ensure!(
expected_types.contains(&frame_type),
Error::UnexpectedFrameType {
frame_type,
expected_types: expected_types.to_vec()
}
);
Ok((frame_type, payload))
}
fn deserialize_frame<F: Frame + serde::de::DeserializeOwned>(frame: Bytes) -> Result<F, Error> {
postcard::from_bytes(&frame).map_err(|err| {
e!(Error::DeserializationError {
frame_type: F::TAG,
source: anyerr!(err)
})
})
}View on GitHub (pinned to 2b4de030ce)