{"record":{"id":"b25e9c42d3ade0d6","repo":"clockworklabs/SpacetimeDB","slug":"failed-to-read-bytes-of-commit-payload","errorCode":null,"errorMessage":"failed to read {} bytes of commit payload: {}","messagePattern":"failed to read (.+?) bytes of commit payload: (.+?)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/commitlog/src/commit.rs","lineNumber":314,"sourceCode":"    /// [`ChecksumMismatch`] is returned.\n    pub fn decode<R: Read>(reader: R) -> io::Result<Option<Self>> {\n        Self::decode_internal(reader, DEFAULT_LOG_FORMAT_VERSION)\n    }\n\n    pub(crate) fn decode_internal<R: Read>(reader: R, log_format_version: u8) -> io::Result<Option<Self>> {\n        let mut reader = Crc32cReader::new(reader);\n\n        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,","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/commitlog/src/commit.rs#L296-L332","documentation":"Emitted by Commit::decode after a commit header promises hdr.len bytes of record payload and read_exact for exactly those bytes fails. The most common cause is a torn write: the process died after the header reached disk but before all payload bytes did, so the log ends mid-commit. The original error kind is preserved and the expected byte count is included in the message.","triggerScenarios":"Decoding a commit whose payload extends past the end of the segment file - typically after a crash or power loss between writing the commit header and its records, or after external truncation of a segment.","commonSituations":"Hard-killed nodes (kill -9, OOM, power cut) replaying their commitlog on restart; segment files truncated by an outside process; disk full at the moment of the write.","solutions":["Let normal recovery run: SpacetimeDB truncates the log back to the last complete commit on open, discarding the torn tail.","If recovery does not clear it, truncate the affected segment to its last valid commit boundary (or remove the damaged newest segment) and restart.","Fix the root cause: stop force-killing the process; verify fsync behavior, disk health and free space."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match reader.next_commit() {\n    Ok(Some(commit)) => commits.push(commit),\n    Ok(None) => {} // end of log\n    Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {\n        // torn tail from a crash: stop at the last complete commit\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Shut nodes down gracefully so the final commit is never half-written.","Do not run tools that truncate segment files under a live node.","Treat UnexpectedEof here as recoverable (torn tail) and other error kinds as real storage faults."],"tags":["rust","commitlog","torn-write","recovery","io"],"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"}