{"record":{"id":"4c0cc301c9e4f794","repo":"n0-computer/iroh","slug":"unexpectedframetype","errorCode":"UnexpectedFrameType","errorMessage":"Unexpected tag, got {frame_type:?}, but expected one of {expected_types:?}","messagePattern":"Unexpected tag, got (.+?), but expected one of (.+?)","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"iroh-relay/src/protos/handshake.rs","lineNumber":586,"sourceCode":"    io.flush()\n        .await\n        .map_err(|err| e!(Error::Websocket, anyerr!(err)))?;\n    Ok(())\n}\n\nasync fn read_frame(\n    io: &mut impl BytesStreamSink,\n    expected_types: &[FrameType],\n) -> Result<(FrameType, Bytes), Error> {\n    let mut payload = io\n        .try_next()\n        .await\n        .map_err(|err| e!(Error::Websocket, anyerr!(err)))?\n        .ok_or_else(|| e!(Error::UnexpectedEnd))?;\n\n    let frame_type = FrameType::from_bytes(&mut payload)?;\n    trace!(?frame_type, \"Reading frame\");\n    ensure!(\n        expected_types.contains(&frame_type),\n        Error::UnexpectedFrameType {\n            frame_type,\n            expected_types: expected_types.to_vec()\n        }\n    );\n\n    Ok((frame_type, payload))\n}\n\nfn deserialize_frame<F: Frame + serde::de::DeserializeOwned>(frame: Bytes) -> Result<F, Error> {\n    postcard::from_bytes(&frame).map_err(|err| {\n        e!(Error::DeserializationError {\n            frame_type: F::TAG,\n            source: anyerr!(err)\n        })\n    })\n}","sourceCodeStart":568,"sourceCodeEnd":604,"githubUrl":"https://github.com/n0-computer/iroh/blob/2b4de030ce5e0133f272871a76f0c685c63f552a/iroh-relay/src/protos/handshake.rs#L568-L604","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Rust\nmatch read_frame(&mut stream, &[FrameType::Ping, FrameType::Pong]).await {\n    Err(err @ Error::UnexpectedFrameType { frame_type, expected_types }) => {\n        tracing::error!(?frame_type, ?expected_types, \"protocol desync — reconnecting\");\n        reconnect().await;\n    }\n    other => other?,\n}","preventionTips":["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."],"tags":["network","protocol","handshake","iroh"],"backgroundTag":"unexpected-response-shape","analyzedSha":"2b4de030ce5e0133f272871a76f0c685c63f552a","analyzedAt":"2026-09-08T04:26:47.755Z","contentChangedAt":"2026-09-08T04:26:47.755Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}