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

  1. 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)
  2. If you fork and raise `MAX_FRAME_LEN`, keep it at or below u32::MAX and revisit this conversion
  3. 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

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


AI-assisted analysis of atuinsh/atuin@15fe1318f1 (2026-08-19). Data as JSON: /api/errors/ae004b7082fd8172. Report an issue: GitHub.