{"record":{"id":"d943a6722691a863","repo":"clockworklabs/SpacetimeDB","slug":"failed-to-read-checksum-e","errorCode":null,"errorMessage":"failed to read checksum: {e}","messagePattern":"failed to read checksum: (.+?)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/commitlog/src/commit.rs","lineNumber":322,"sourceCode":"        let v = if log_format_version == 0 {\n            Version::V0\n        } else {\n            Version::V1\n        };\n        let Some(hdr) = Header::decode_internal(&mut reader, v)? else {\n            return Ok(None);\n        };\n        let mut records = vec![0; hdr.len as usize];\n        reader.read_exact(&mut records).map_err(|e| {\n            io::Error::new(\n                e.kind(),\n                format!(\"failed to read {} bytes of commit payload: {}\", hdr.len, e),\n            )\n        })?;\n\n        let chk = reader.crc32c();\n        let crc = decode_u32(reader.into_inner())\n            .map_err(|e| io::Error::new(e.kind(), format!(\"failed to read checksum: {e}\")))?;\n\n        if chk != crc {\n            return Err(invalid_data(ChecksumMismatch));\n        }\n\n        Ok(Some(Self {\n            min_tx_offset: hdr.min_tx_offset,\n            epoch: hdr.epoch,\n            n: hdr.n,\n            records,\n            checksum: crc,\n        }))\n    }\n\n    /// Convert `self` into an iterator yielding [`Transaction`]s.\n    ///\n    /// The supplied [`Decoder`] is responsible for extracting individual\n    /// transactions from the `records` buffer.","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/commitlog/src/commit.rs#L304-L340","documentation":"Raised at the last step of Commit::decode: after the record payload, every commit stores a 4-byte little-endian CRC32C checksum, and reading those bytes failed (kind preserved). Because the checksum is the final thing written for a commit, this almost always means the log ends in the middle of a commit - the header and payload landed but the checksum did not.","triggerScenarios":"A commit whose header and payload decoded fine but whose trailing 4 checksum bytes are missing because the write was interrupted (crash/power loss), or the segment file was externally truncated or partially copied.","commonSituations":"Same torn-write scenarios as the payload-read error, one step later; also seen when backups copy a segment while it is being written.","solutions":["Rely on the node's truncation-on-recovery to drop the incomplete commit; restart the node cleanly.","If it persists, truncate the segment to the last valid commit and restart.","Ensure backups copy segment files only while the node is stopped, and no external process truncates the data directory."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match reader.next_commit() {\n    Ok(Some(commit)) => commits.push(commit),\n    Ok(None) => {}\n    Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {\n        // checksum bytes missing: torn final commit - accept the prefix and stop\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Take backups of the data directory only while the node is stopped, so segments are never copied mid-commit.","Ensure durable writes (fsync) and healthy disks so commit checksums always land.","On recovery, accept the last complete commit and let the node truncate the torn tail."],"tags":["rust","commitlog","checksum","torn-write","recovery"],"backgroundTag":"truncated-commit-log","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}