atuinsh/atuin · info
payload length fits in u32
Error message
payload length fits in u32
What it means
`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.
Source
Thrown at crates/atuin-pty-proxy/src/protocol.rs:123
#[must_use]
pub fn classify_greeting(first: &[u8]) -> Greeting {
if first == MAGIC {
Greeting::V2
} else {
Greeting::Legacy
}
}
/// Encode a frame header + payload.
///
/// # Panics
///
/// Panics if `payload` exceeds [`MAX_FRAME_LEN`]. Callers own the payload
/// sizes (PTY read chunks and screen snapshots) and must cap them first.
#[must_use]
pub fn encode_frame(frame_type: u8, payload: &[u8]) -> Vec<u8> {
assert!(payload.len() <= MAX_FRAME_LEN, "frame payload exceeds MAX_FRAME_LEN");
let len = u32::try_from(payload.len()).expect("payload length fits in u32");
let mut buf = Vec::with_capacity(5 + payload.len());
buf.push(frame_type);
buf.extend_from_slice(&len.to_be_bytes());
buf.extend_from_slice(payload);
buf
}
/// Read one frame. Returns `Ok(None)` on a clean EOF at a frame boundary.
///
/// Unknown frame types are returned as-is: the transport layer does not
/// decide policy (the server closes on unknown client frames; clients skip
/// unknown server frames for forward compatibility).
///
/// # Errors
///
/// Fails on EOF mid-frame, on a length above [`MAX_FRAME_LEN`], or on any
/// underlying read error.
pub fn read_frame(reader: &mut impl Read) -> io::Result<Option<(u8, Vec<u8>)>> {View on GitHub (pinned to 15fe1318f1)
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
Example fix
// before
let frame = encode_frame(frame_type, &blob);
// after
if blob.len() > protocol::MAX_FRAME_LEN {
return Err(FrameTooLarge(blob.len()));
}
let frame = encode_frame(frame_type, &blob); Defensive patterns
Strategy: validation
Validate before calling
// Cap payload size before encoding; chunk oversize blobs (as screen.rs does)
if payload.len() > protocol::MAX_FRAME_LEN {
return Err(FrameTooLarge(payload.len()));
}
let frame = encode_frame(frame_type, payload); Type guard
fn frame_safe(payload: &[u8]) -> bool {
payload.len() <= protocol::MAX_FRAME_LEN // 1 MiB
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- overflowed maximum capacity
- exceeded maximum length
- issue in stats average query
- issue in stats exits query
- issue in stats day of week query
AI-assisted analysis of atuinsh/atuin@15fe1318f1 (2026-08-19).
Data as JSON: /api/errors/ae004b7082fd8172.
Report an issue: GitHub.