clockworklabs/SpacetimeDB · error · std::io::Error
invalid seek to a negative or overflowing position
Error message
invalid seek to a negative or overflowing position
What it means
The in-memory Segment implements io::Seek with std::io::Cursor semantics. SeekFrom::Start is always allowed (even past the end), but a relative seek (SeekFrom::Current or SeekFrom::End) whose signed offset would move the position below zero, or overflow u64, fails base_pos.checked_add_signed(offset) and returns io::ErrorKind::InvalidInput with this message.
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 6dee26c6ef)
Solutions
- Compute the absolute target first (stream_position()/len() + checked_add_signed) and clamp to 0 before issuing SeekFrom::Start
- Guard relative seeks: only seek End(-n)/Current(-n) when n <= len()/stream_position()
- Use u64 arithmetic and SeekFrom::Start instead of signed Current/End offsets
Example fix
// before segment.seek(io::SeekFrom::Current(-(header_len as i64)))?; // after let pos = segment.stream_position()?; let target = pos.saturating_sub(header_len as u64); segment.seek(io::SeekFrom::Start(target))?;
Defensive patterns
Strategy: validation
Validate before calling
let pos = segment.stream_position()?; let len = segment.seek(io::SeekFrom::End(0))?; // compute the target absolutely, clamped to the segment bounds let target = (pos as i64 + delta).clamp(0, len as i64) as u64; segment.seek(io::SeekFrom::Start(target))?;
Type guard
fn is_invalid_seek(err: &io::Error) -> bool {
err.kind() == io::ErrorKind::InvalidInput
&& err.to_string().contains("negative or overflowing position")
} Try / catch
let new_pos = match segment.seek(rel) {
Ok(p) => p,
Err(e) if is_invalid_seek(&e) => {
// recover by clamping to start
segment.seek(io::SeekFrom::Start(0))?
}
Err(e) => return Err(e),
}; Prevention
- Prefer SeekFrom::Start with absolute u64 positions computed via checked/saturating arithmetic
- Never subtract first in i64 when the subtrahend may exceed the position; use u64 saturating_sub
- For End-relative seeks, check len() >= n before issuing seek(End(-n))
When it happens
Trigger: seek(SeekFrom::End(-n)) with n greater than the segment length; seek(SeekFrom::Current(-n)) with n greater than the current position; an offset computed via i64 subtraction that underflows (e.g. pos - extra as i64 when extra > pos).
Common situations: Rewind-by-N logic after reading variable-length records; subtracting a header/checksum size from a position that is smaller than it; porting seek code that assumed clamping instead of erroring.
Related errors
- segment {offset} already exists
- segment {offset} does not exist
- no space left on device
- invalid seek to a negative or overflowing position
- invalid transaction offset {}, expected {}
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/06988d4d694045c3.
Report an issue: GitHub.