{"record":{"id":"91ce31ed46119071","repo":"clockworklabs/SpacetimeDB","slug":"failed-to-decode-commit-header-field-field-e","errorCode":null,"errorMessage":"failed to decode commit header field '{field}': {e}","messagePattern":"failed to decode commit header field '(.+?)': (.+?)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/commitlog/src/commit.rs","lineNumber":413,"sourceCode":"        let size_in_bytes = Commit::from(commit).encoded_len() as u64;\n\n        Self {\n            tx_range,\n            size_in_bytes,\n            epoch,\n            checksum,\n        }\n    }\n}\n\nfn decode_u32<R: Read>(mut read: R) -> io::Result<u32> {\n    let mut buf = [0; 4];\n    read.read_exact(&mut buf)?;\n    Ok(u32::from_le_bytes(buf))\n}\n\nfn decode_header_error(e: DecodeError, field: &str) -> io::Error {\n    invalid_data(format!(\"failed to decode commit header field '{field}': {e}\"))\n}\n\nfn invalid_data<E>(e: E) -> io::Error\nwhere\n    E: Into<Box<dyn std::error::Error + Send + Sync>>,\n{\n    io::Error::new(io::ErrorKind::InvalidData, e)\n}\n\n#[cfg(test)]\nmod tests {\n    use std::num::NonZeroU8;\n\n    use proptest::prelude::*;\n\n    use super::*;\n    use crate::{payload::ArrayDecoder, tests::helpers::enable_logging, DEFAULT_LOG_FORMAT_VERSION};\n","sourceCodeStart":395,"sourceCodeEnd":431,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/9e0d92412ff2248f401a8ad12d535f2b5ac30912/crates/commitlog/src/commit.rs#L395-L431","documentation":"Defensive decoder error from Header decoding: the fixed header buffer is parsed field-by-field (min_tx_offset u64, epoch u64, n u16, len u32) and a DecodeError from spacetimedb_sats::buffer is wrapped with the failing field's name via decode_header_error. Because the header is read into a stack buffer sized exactly Header::LEN (22 bytes in v1, 14 in v0 - exactly covering the fields), this path cannot fire for on-disk data; it only triggers if the buffer length and the decode sequence disagree, i.e. a crate bug or version skew.","triggerScenarios":"Effectively unreachable through public APIs: it would require decoding a header from a buffer shorter than the fields being read, which the fixed-size read_exact makes impossible. Realistic only with a vendored/patched commitlog whose Header::LEN differs from the field sequence, or mixed crate versions writing/reading the log.","commonSituations":"Mixing commitlog crate versions (forks with a different header layout); otherwise not observed.","solutions":["Align all workspace members and nodes on a single SpacetimeDB/commitlog version and rebuild from clean state.","Report upstream with the exact field name from the message - it identifies which read overran and pinpoints the layout mismatch."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match reader.next_commit() {\n    Ok(Some(c)) => commits.push(c),\n    Ok(None) => {}\n    Err(e) if e.kind() == io::ErrorKind::InvalidData\n        && e.to_string().contains(\"failed to decode commit header field\") =>\n    {\n        // layout/version skew inside the crate: unrecoverable - stop and escalate\n        return Err(e.into());\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Pin a single SpacetimeDB/commitlog version across all workspace members and nodes.","Never read logs written by a different (especially vendored/patched) commitlog version.","If this fires, capture the field name from the message - it identifies the layout mismatch for an upstream report."],"tags":["rust","commitlog","decode","defensive","version-skew"],"backgroundTag":"malformed-log-header","analyzedSha":"9e0d92412ff2248f401a8ad12d535f2b5ac30912","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}