tokio-rs/tokio · error

filled overflow

Error message

filled overflow

What it means

ReadBuf::advance computes self.filled.checked_add(n).expect('filled overflow'). It panics if advancing the filled region by n would overflow usize — almost always because n is huge (garbage) or the filled counter was already corrupted. set_filled(new) then enforces filled <= initialized.

Source

Thrown at tokio/src/io/read_buf.rs:203

    /// Clears the buffer, resetting the filled region to empty.
    ///
    /// The number of initialized bytes is not changed, and the contents of the buffer are not modified.
    #[inline]
    pub fn clear(&mut self) {
        self.filled = 0;
    }

    /// Advances the size of the filled region of the buffer.
    ///
    /// The number of initialized bytes is not changed.
    ///
    /// # Panics
    ///
    /// Panics if the filled region of the buffer would become larger than the initialized region.
    #[inline]
    #[track_caller]
    pub fn advance(&mut self, n: usize) {
        let new = self.filled.checked_add(n).expect("filled overflow");
        self.set_filled(new);
    }

    /// Sets the size of the filled region of the buffer.
    ///
    /// The number of initialized bytes is not changed.
    ///
    /// Note that this can be used to *shrink* the filled region of the buffer in addition to growing it (for
    /// example, by a `AsyncRead` implementation that compresses data in-place).
    ///
    /// # Panics
    ///
    /// Panics if the filled region of the buffer would become larger than the initialized region.
    #[inline]
    #[track_caller]
    pub fn set_filled(&mut self, n: usize) {
        assert!(
            n <= self.initialized,

View on GitHub (pinned to 625954f365)

Solutions

  1. Pass exactly the number of bytes newly written into the buffer (e.g. res from read).
  2. Validate n <= buf.remaining() / initialized region before calling advance.
  3. Use ReadBuf helpers (put_slice, put_u8, etc.) which manage filled correctly.
  4. Add assertions/debug logging of n in your AsyncRead impl to catch corruption early.

Example fix

// before
let n = read(&mut buf[written..])?;
read_buf.advance(buf.len()); // wrong: full length, overflow risk
// after
let n = read(&mut buf[written..])?;
read_buf.advance(n); // only bytes actually read
Defensive patterns

Strategy: validation

Validate before calling

// Validate n against the buffer's capacity before advancing:
let n = read(&mut buf[..])?;
assert!(n <= read_buf.remaining(), "advance too large");
read_buf.advance(n);

Type guard

fn safe_advance(buf: &mut tokio::io::ReadBuf<'_>, n: usize) -> Result<(), io::Error> {
    if n > buf.remaining() {
        return Err(io::Error::new(io::ErrorKind::InvalidInput, "advance exceeds remaining"));
    }
    buf.advance(n);
    Ok(())
}

Try / catch

// Use catch_unwind if you cannot trust the upstream AsyncRead:
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    read_buf.advance(n);
}))

Prevention

When it happens

Trigger: Calling read_buf.advance(n) with an absurd n (e.g. result of a buggy size computation, or passing bytes_read from a failed read); double-counting bytes already accounted for.

Common situations: AsyncRead impl that advances by the buffer length instead of the bytes actually read; passing buf.remaining() instead of n; integer underflow producing wraparound then overflow; off-by-one in a parser.

Related errors


AI-assisted analysis of tokio-rs/tokio@625954f365 (2026-08-11). Data as JSON: /api/errors/4e061bf36d64adf2. Report an issue: GitHub.