{"record":{"id":"ae004b7082fd8172","repo":"atuinsh/atuin","slug":"payload-length-fits-in-u32","errorCode":null,"errorMessage":"payload length fits in u32","messagePattern":"payload length fits in u32","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"crates/atuin-pty-proxy/src/protocol.rs","lineNumber":123,"sourceCode":"#[must_use]\npub fn classify_greeting(first: &[u8]) -> Greeting {\n    if first == MAGIC {\n        Greeting::V2\n    } else {\n        Greeting::Legacy\n    }\n}\n\n/// Encode a frame header + payload.\n///\n/// # Panics\n///\n/// Panics if `payload` exceeds [`MAX_FRAME_LEN`]. Callers own the payload\n/// sizes (PTY read chunks and screen snapshots) and must cap them first.\n#[must_use]\npub fn encode_frame(frame_type: u8, payload: &[u8]) -> Vec<u8> {\n    assert!(payload.len() <= MAX_FRAME_LEN, \"frame payload exceeds MAX_FRAME_LEN\");\n    let len = u32::try_from(payload.len()).expect(\"payload length fits in u32\");\n    let mut buf = Vec::with_capacity(5 + payload.len());\n    buf.push(frame_type);\n    buf.extend_from_slice(&len.to_be_bytes());\n    buf.extend_from_slice(payload);\n    buf\n}\n\n/// Read one frame. Returns `Ok(None)` on a clean EOF at a frame boundary.\n///\n/// Unknown frame types are returned as-is: the transport layer does not\n/// decide policy (the server closes on unknown client frames; clients skip\n/// unknown server frames for forward compatibility).\n///\n/// # Errors\n///\n/// Fails on EOF mid-frame, on a length above [`MAX_FRAME_LEN`], or on any\n/// underlying read error.\npub fn read_frame(reader: &mut impl Read) -> io::Result<Option<(u8, Vec<u8>)>> {","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/atuinsh/atuin/blob/15fe1318f1df51de604262eb50734c9883d48e7b/crates/atuin-pty-proxy/src/protocol.rs#L105-L141","documentation":"`encode_frame` writes the frame length as a big-endian u32 after first asserting `payload.len() <= MAX_FRAME_LEN` (1 MiB, defined in the same file). The follow-up `u32::try_from(payload.len()).expect(\"payload length fits in u32\")` is belt-and-braces: any payload large enough to fail the conversion would already have failed the 1 MiB assert, so with the current constant this expect is effectively unreachable.","triggerScenarios":"Only if `MAX_FRAME_LEN` were ever raised above 4 GiB on a 64-bit platform. A payload between 1 MiB and 4 GiB hits the preceding `assert!` ('frame payload exceeds MAX_FRAME_LEN') instead of this line.","commonSituations":"Developers hitting oversize-frame panics see the assert message, not this one; encountering this exact message implies a modified MAX_FRAME_LEN or an upstream refactor of encode_frame.","solutions":["Cap payloads at `MAX_FRAME_LEN` (1 MiB) before calling `encode_frame` - the sibling assert is the real limit (screen.rs already chunks oversize snapshots)","If you fork and raise `MAX_FRAME_LEN`, keep it at or below u32::MAX and revisit this conversion","Pre-check `payload.len() <= protocol::MAX_FRAME_LEN` at your call site and chunk if needed"],"exampleFix":"// before\nlet frame = encode_frame(frame_type, &blob);\n\n// after\nif blob.len() > protocol::MAX_FRAME_LEN {\n    return Err(FrameTooLarge(blob.len()));\n}\nlet frame = encode_frame(frame_type, &blob);","handlingStrategy":"validation","validationCode":"// Cap payload size before encoding; chunk oversize blobs (as screen.rs does)\nif payload.len() > protocol::MAX_FRAME_LEN {\n    return Err(FrameTooLarge(payload.len()));\n}\nlet frame = encode_frame(frame_type, payload);","typeGuard":"fn frame_safe(payload: &[u8]) -> bool {\n    payload.len() <= protocol::MAX_FRAME_LEN // 1 MiB\n}","tryCatchPattern":null,"preventionTips":["Treat 1 MiB as the hard frame limit; chunk screen snapshots and PTY bursts at the call site","If you fork the protocol and raise MAX_FRAME_LEN, keep it within u32 and re-audit this conversion","Distinguish the two panics: 'frame payload exceeds MAX_FRAME_LEN' is the real limit; 'payload length fits in u32' means a fork changed the constant"],"tags":["rust","panic","integer-overflow","protocol","framing","defensive-code"],"backgroundTag":"integer-overflow","analyzedSha":"15fe1318f1df51de604262eb50734c9883d48e7b","analyzedAt":"2026-08-19T08:56:57.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}