herdrdev/herdr · error · io::Error

stream frame header is too large

Error message

stream frame header is too large

What it means

While reading a graphics stream frame header, each byte is appended until newline; if the accumulated line exceeds max_bytes before the newline arrives, read_line rejects it with ErrorKind::InvalidData and 'stream frame header is too large'. This bounds header memory per frame. It almost always means the two sides disagree on framing — the reader is consuming what it thinks is a header but the writer is emitting something else (e.g. binary body bytes or a much longer line format).

Source

Thrown at src/api/server/pane_graphics_stream.rs:417

                    wait.on_progress();
                    let now = Instant::now();
                    let total_deadline_at =
                        *total_deadline.get_or_insert_with(|| now + total_timeout);
                    idle_deadline = Some(now + idle_timeout);
                    if now >= total_deadline_at {
                        return Err(io::Error::new(
                            io::ErrorKind::TimedOut,
                            "timed out reading stream frame header",
                        ));
                    }
                    bytes.push(byte[0]);
                    if byte[0] == b'\n' {
                        return String::from_utf8(bytes)
                            .map(Some)
                            .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err));
                    }
                    if bytes.len() > max_bytes {
                        return Err(io::Error::new(
                            io::ErrorKind::InvalidData,
                            "stream frame header is too large",
                        ));
                    }
                }
                Err(err) if read_should_retry(&err) => {
                    wait.after_retry(idle_deadline, total_deadline);
                }
                Err(err) if is_connection_closed_error(&err) => return Ok(None),
                Err(err) => return Err(err),
            }
        }
    })
}

fn read_exact(
    stream: &mut LocalStream,
    len: usize,

View on GitHub (pinned to f457cff4f2)

Solutions

  1. Verify both reader and writer use the same stream framing protocol version (header line length cap and format).
  2. Log the first ~max_bytes of the accumulated line to see what is actually being read — it usually reveals body bytes or a different message format.
  3. After any earlier frame error, resynchronize by discarding until the next newline instead of continuing mid-stream.
  4. If legitimate headers now exceed the cap, raise max_bytes for read_line consistently on both sides.

Example fix

// before
let header = read_line(&mut stream, idle, total, 128)?;

// after
let header = read_line(&mut stream, idle, total, 4096)?;
Defensive patterns

Strategy: validation

Validate before calling

// producer side: assert header fits the reader's cap before writing
let header = format!("{json}\n");
assert!(header.len() <= HEADER_MAX_BYTES, "header {} > cap {}", header.len(), HEADER_MAX_BYTES);

Type guard

fn is_valid_header_line(line: &str) -> bool {
    !line.is_empty() && line.len() <= HEADER_MAX_BYTES && line.ends_with('\n')
}

Try / catch

match read_line(&mut stream, idle, total).await {
    Err(e) if e.kind() == io::ErrorKind::InvalidData => {
        // framing desync: dump the partial line, resync to next newline or reopen stream
    }
    other => other,
}

Prevention

When it happens

Trigger: serve_frames parsing a stream where the expected header line never terminates within max_bytes: a protocol version mismatch emitting longer headers, a binary payload read as a header line, or a corrupt stream that lost the previous frame's newline.

Common situations: Upgrading one side of the graphics protocol but not the other (longer JSON headers); a desync after a partially-written earlier frame; a producer emitting raw image bytes where a header line was expected; tests with fixture headers exceeding the configured cap.

Related errors


AI-assisted analysis of herdrdev/herdr@f457cff4f2 (2026-08-28). Data as JSON: /api/errors/27528b323265809c. Report an issue: GitHub.