clockworklabs/SpacetimeDB · error · io::Error
invalid seek to a negative or overflowing position
Error message
invalid seek to a negative or overflowing position
What it means
Segment implements io::Seek by translating SeekFrom::End/Current into base_pos + signed offset via checked_add_signed. SeekFrom::Start(n) always succeeds (any u64 is a valid absolute position in the in-memory store). For End/Current seeks, if the signed offset makes the result negative or exceed u64::MAX, the seek returns ErrorKind::InvalidInput, 'invalid seek to a negative or overflowing position', mirroring std's own Cursor seek semantics.
Source
Thrown at crates/dst/src/sim/commitlog.rs:356
}
}
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 u64)
}
}
impl FileLike for Segment {
fn fsync(&mut self) -> io::Result<()> {
Ok(())
}
View on GitHub (pinned to 6dee26c6ef)
Solutions
- Clamp negative rewinds: use seek(SeekFrom::Current(-n)) only after verifying pos >= n, or compute the absolute target and use SeekFrom::Start
- Validate lengths/offsets parsed from the data before turning them into seek offsets
- For End-relative seeks, confirm segment len >= |negative offset|
Example fix
// before
seg.seek(io::SeekFrom::Current(-(header_len as i64)))?; // InvalidInput if pos < header_len
// after
let pos = seg.stream_position()?;
let target = pos.checked_sub(header_len as u64)
.ok_or_else(|| anyhow!("record header crosses segment start"))?
.min(seg.stream_len()?);
seg.seek(io::SeekFrom::Start(target))?; Defensive patterns
Strategy: validation
Validate before calling
async fn checked_seek<S: std::io::Seek + std::io::Read>(s: &mut S, target: i64) -> std::io::Result<u64> {
let (base, len) = (s.stream_position()?, s.stream_len()?);
let abs = i128::from(base) + i128::from(target);
anyhow::ensure!((0..=i128::from(len)).contains(&abs), "seek out of range");
s.seek(std::io::SeekFrom::Start(abs as u64))
} Try / catch
match seg.seek(io::SeekFrom::Current(off)) {
Ok(p) => p,
Err(ref e) if e.kind() == std::io::ErrorKind::InvalidInput => {
return Err(anyhow::anyhow!("rewound past segment start (pos={}, off={})", pos, off));
}
Err(e) => return Err(e.into()),
} Prevention
- Prefer absolute SeekFrom::Start with a validated target over negative relative seeks
- Validate length prefixes parsed from log records before using them as offsets
- Clamp rewinds: min(requested, current_position)
When it happens
Trigger: seek(SeekFrom::Current(negative)) when the current position is smaller than |offset| (would go below zero); seek(SeekFrom::End(negative)) past the start of the data; seek with a positive offset that overflows u64 from a large base; rewinding a fresh, zero-length segment with SeekFrom::End(-1) or more.
Common situations: Generic log-reading code that rewinds by a fixed byte count (e.g. re-reading a trailer/record header) without checking it cannot precede the segment start; parsers computing offsets from untrusted length prefixes; copying std::io::Cursor patterns onto Segment.
Related errors
- segment {offset} already exists
- segment {offset} does not exist
- write position overflow
- allocation size overflow
- no space left on device
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/f32b747524378e04.
Report an issue: GitHub.