clockworklabs/SpacetimeDB · error · io::Error

InvalidInput

InvalidInput

Error message

invalid seek to a negative or overflowing position

What it means

A Seek on an in-memory segment computed a target position that is negative (before byte 0) or overflows u64, so checked_add_signed failed (InvalidInput). This mirrors std's seek semantics: SeekFrom::Start(n) never fails here; only End/Current arithmetic can. The segment's own state is unchanged.

Source

Thrown at crates/commitlog/src/repo/mem/segment.rs:167

    }
}

impl io::Seek for Segment {
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
        let (base_pos, offset) = match pos {
            io::SeekFrom::Start(n) => {
                self.pos = n;
                return Ok(n);
            }
            io::SeekFrom::End(n) => (self.len() as u64, n),
            io::SeekFrom::Current(n) => (self.pos, n),
        };
        match base_pos.checked_add_signed(offset) {
            Some(n) => {
                self.pos = n;
                Ok(n)
            }
            None => Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "invalid seek to a negative or overflowing position",
            )),
        }
    }
}

impl SegmentLen for Segment {
    fn segment_len(&mut self) -> io::Result<u64> {
        Ok(self.len() as _)
    }
}

impl FileLike for Segment {
    fn fsync(&mut self) -> io::Result<()> {
        Ok(())
    }

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Compute the absolute target with checked/saturating u64 arithmetic first, then seek with SeekFrom::Start
  2. Clamp rewind amounts: target = len.saturating_sub(rewind)
  3. Validate externally sourced offsets against the segment length before seeking

Example fix

// before
reader.seek(io::SeekFrom::End(-(skip as i64)))?; // fails when skip > len

// after
let len = reader.seek(io::SeekFrom::End(0))?;
let target = len.saturating_sub(skip);
reader.seek(io::SeekFrom::Start(target))?;
Defensive patterns

Strategy: validation

Validate before calling

// compute absolute targets instead of relative seeks
fn rewind_to(reader: &mut impl io::Seek, skip: u64) -> io::Result<u64> {
    let len = reader.seek(io::SeekFrom::End(0))?;
    let target = len.saturating_sub(skip); // clamp at 0
    reader.seek(io::SeekFrom::Start(target))
}

Type guard

fn is_invalid_seek(e: &io::Error) -> bool {
    e.kind() == io::ErrorKind::InvalidInput
        && e.to_string().contains("invalid seek to a negative or overflowing position")
}

Prevention

When it happens

Trigger: seek(SeekFrom::End(-n)) with n greater than the segment length; seek(SeekFrom::Current(negative)) that would move before position 0; relative skips computed from untrusted or underflowing i64 offsets.

Common situations: Replay/scan code that rewinds by a delta larger than the file; converting external i64 offsets into seeks without clamping.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/fb08f379a8f355d0. Report an issue: GitHub.