clockworklabs/SpacetimeDB · error · io::Error
mismatch key in index offset file: expected=
Error message
mismatch key in index offset file: expected={} actual={} What it means
validate_commit_at_offset successfully decodes a commit header at the index's byte offset, but its tx_range.start differs from the tx_offset key stored in the index. The entry maps a transaction offset to the wrong byte position, returning io::ErrorKind::InvalidData with expected/actual values. This is the detectable form of a stale offset index after a crash or partial restore.
Solutions
- Rebuild the offset index by deleting index files and reopening the log
- Restore segment and index files only as matched pairs from the same snapshot
- Handle the error by falling back to a linear scan for the requested offset
- Log expected vs actual offsets to confirm which direction the index is stale
Defensive patterns
Strategy: fallback
Type guard
fn is_index_offset_mismatch(err: &io::Error) -> bool {
err.kind() == io::ErrorKind::InvalidData && err.to_string().contains("mismatch key in index offset file")
} Try / catch
match validate_commit_at_offset(&mut reader, tx_offset, byte_offset) {
Ok(meta) => Ok(meta),
Err(e) if is_index_offset_mismatch(&e) => {
// entry maps to the wrong byte position: scan from the previous valid entry
scan_for_tx(&mut reader, tx_offset)
}
Err(e) => Err(e),
} Prevention
- Restore index and segment files only from the same snapshot
- Log expected vs actual tx offsets when validation fails to measure index staleness
- Rebuild indexes rather than patching individual entries
When it happens
Trigger: Index entry not yet updated after commits were rewritten at new byte positions (async index fsync plus crash); segment and index from different backup generations; concurrent external modification of segment files.
Common situations: Unclean shutdown between segment write and index flush; restoring a segment file but keeping the old index; copying logs between nodes with mismatched index state.
Related errors
- mismatched key in offset index file
- failed to decode commit
- No valid commit found in index up to key
- all-zeroes commit header
- Failed to truncate offset index
AI-assisted analysis of clockworklabs/SpacetimeDB@3653d2ed49 (2026-08-20).
Data as JSON: /api/errors/8f92bf7b0c744d6a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/commitlog/src/segment.rs:775
))
}
/// Validates and decodes a commit at `byte_offset` in the segment.
///
/// # Returns
/// * `Ok(commit::Metadata)` - If a valid commit is found with matching transaction offset
/// * `Err` - If commit can't be decoded or has mismatched transaction offset
fn validate_commit_at_offset<R: io::Read + io::Seek>(
reader: &mut R,
tx_offset: TxOffset,
byte_offset: u64,
) -> io::Result<commit::Metadata> {
reader.seek(SeekFrom::Start(byte_offset))?;
let commit = commit::Metadata::extract(reader)?
.ok_or_else(|| io::Error::new(ErrorKind::InvalidData, "failed to decode commit"))?;
if commit.tx_range.start != tx_offset {
return Err(io::Error::new(
ErrorKind::InvalidData,
format!(
"mismatch key in index offset file: expected={} actual={}",
tx_offset, commit.tx_range.start
),
));
}
Ok(commit)
}
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use pretty_assertions::assert_matches;
use spacetimedb_paths::server::CommitLogDir;
use tempfile::tempdir;View on GitHub (pinned to 3653d2ed49)