{"record":{"id":"09a8f5d1cad4d928","repo":"clockworklabs/SpacetimeDB","slug":"mismatched-key-in-offset-index-file","errorCode":null,"errorMessage":"mismatched key in offset index file","messagePattern":"mismatched key in offset index file","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/commitlog/src/segment.rs","lineNumber":515,"sourceCode":"    start_tx_offset: u64,\n) -> Result<u64, IndexError> {\n    let (index_key, byte_offset) = index_file.key_lookup(start_tx_offset)?;\n\n    // If the index_key is 0, it means the index file is empty, return error without seeking\n    if index_key == 0 {\n        return Err(IndexError::KeyNotFound);\n    }\n    debug!(\"index lookup for key={start_tx_offset}: found key={index_key} at byte-offset={byte_offset}\");\n    // returned `index_key` should never be greater than `start_tx_offset`\n    debug_assert!(index_key <= start_tx_offset);\n\n    // Check if the offset index is pointing to the right commit.\n    let hdr = validate_commit_at_byte_offset(&mut segment, byte_offset)?;\n    if hdr.min_tx_offset == index_key {\n        // Advance the segment Seek if expected commit is found.\n        segment.seek(SeekFrom::Start(byte_offset))\n    } else {\n        Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            \"mismatched key in offset index file\",\n        ))\n    }\n    .map_err(Into::into)\n}\n\n/// Try to extract the commit header from the asked position without advancing seek.\n/// `IndexFileMut` fsync asynchoronously, which makes it important for reader to verify its entry\nfn validate_commit_at_byte_offset<Reader: io::Read + io::Seek>(\n    mut reader: &mut Reader,\n    byte_offset: u64,\n) -> io::Result<commit::Header> {\n    let pos = reader.stream_position()?;\n    reader.seek(SeekFrom::Start(byte_offset))?;\n\n    let hdr_or_error = StoredCommit::decode(&mut reader).and_then(|maybe_commit| {\n        let StoredCommit {","sourceCodeStart":497,"sourceCodeEnd":533,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/3653d2ed498fddbd83b7062aa6bf8970e18635ec/crates/commitlog/src/segment.rs#L497-L533","documentation":"After an offset-index lookup for a transaction offset, the reader seeks to the recorded byte offset and re-reads the commit header to verify it. If the commit's min_tx_offset does not equal the index key, the index disagrees with the segment data and io::ErrorKind::InvalidData 'mismatched key in offset index file' is returned. Verification exists because IndexFileMut fsyncs asynchronously, so after a crash an entry can point at a stale position.","triggerScenarios":"Crash between writing commit data to the segment and flushing the offset index; index file truncated or partially written; copying a segment file into the repo without its matching index (or vice versa); seeking an old tx offset whose index entry predates a segment rewrite.","commonSituations":"Unclean shutdowns; restoring backups that mix segment generations; manual file manipulation in the log directory.","solutions":["Delete the offset index files so they are rebuilt from segment data on next open","Always copy segment and its index together when archiving/restoring","Fall back to the backwards-scan path (as done around segment.rs:745) or a full segment scan when the direct seek fails","Report the (key, byte_offset, found min_tx_offset) triple for diagnosis"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":"fn is_index_key_mismatch(err: &io::Error) -> bool {\n    err.kind() == io::ErrorKind::InvalidData && err.to_string().contains(\"mismatched key in offset index file\")\n}","tryCatchPattern":"match segment.seek_to_tx(tx_offset).await {\n    Ok(pos) => Ok(pos),\n    Err(e) if is_index_key_mismatch(&e) => {\n        // index is untrustworthy: fall back to a full sequential scan of the segment\n        scan_segment_for_tx(&mut segment, tx_offset).await\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Archive/restore segment files and their offset index files only as matched pairs","Provide a rebuild-index maintenance operation and run it after any restore","Treat any index validation failure as 'use scan', not as fatal reader error"],"tags":["rust","commitlog","offset-index","mismatch","invalid-data"],"backgroundTag":"index-corruption","analyzedSha":"3653d2ed498fddbd83b7062aa6bf8970e18635ec","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}