{"record":{"id":"a084b209e9b14576","repo":"clockworklabs/SpacetimeDB","slug":"failed-to-read-segment-header-bytes","errorCode":null,"errorMessage":"failed to read segment header ({} bytes): {}","messagePattern":"failed to read segment header \\((.+?) bytes\\): (.+?)","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/commitlog/src/segment.rs","lineNumber":50,"sourceCode":"pub struct Header {\n    pub log_format_version: u8,\n    pub checksum_algorithm: u8,\n}\n\nimpl Header {\n    pub const LEN: usize = MAGIC.len() + /* log_format_version + checksum_algorithm + reserved + reserved */ 4;\n\n    pub fn write<W: io::Write>(&self, mut out: W) -> io::Result<()> {\n        out.write_all(&MAGIC)?;\n        out.write_all(&[self.log_format_version, self.checksum_algorithm, 0, 0])?;\n\n        Ok(())\n    }\n\n    pub fn decode<R: io::Read>(mut read: R) -> io::Result<Self> {\n        let mut buf = [0; Self::LEN];\n        read.read_exact(&mut buf).map_err(|e| {\n            io::Error::new(\n                e.kind(),\n                format!(\"failed to read segment header ({} bytes): {}\", Self::LEN, e),\n            )\n        })?;\n\n        if !buf.starts_with(&MAGIC) {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"segment header does not start with magic: expected {:02x?}, got {:02x?}\",\n                    MAGIC,\n                    &buf[..MAGIC.len()]\n                ),\n            ));\n        }\n\n        Ok(Self {\n            log_format_version: buf[MAGIC.len()],","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/commitlog/src/segment.rs#L32-L68","documentation":"Header::decode could not read the fixed 10-byte segment header: read_exact failed and the original ErrorKind and message pass through - typically UnexpectedEof when the file is shorter than 10 bytes, otherwise a genuine I/O error. It fires when a segment file exists but is truncated, empty, or partially written.","triggerScenarios":"Opening a segment file of 0-9 bytes (crash between file creation and header write, or truncation); an I/O error from the device while reading the header; a foreign/renamed file sitting where a segment is expected.","commonSituations":"Power loss during segment creation; restoring from an incomplete backup; files truncated after a disk-full event.","solutions":["Stat the file: if its size is under 10 bytes it is a crash remnant holding no commits - quarantine/delete it and reopen","If the size looks right, chase the inner I/O error (device health, permissions)","Restore the segment from backup if it was expected to contain committed data"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// reject sub-header files before trying to open them as segments\nconst SEGMENT_HEADER_LEN: u64 = 10; // segment::Header::LEN\nfor entry in std::fs::read_dir(dir)? {\n    let p = entry?.path();\n    if p.extension().is_none_or(|e| e != \"idx\" && e != \"lock\") {\n        if let Ok(md) = std::fs::metadata(&p) {\n            if md.len() < SEGMENT_HEADER_LEN {\n                return Err(io::Error::new(io::ErrorKind::InvalidData, format!(\"{}: too short to be a segment\", p.display())));\n            }\n        }\n    }\n}","typeGuard":"fn is_header_read_failure(e: &io::Error) -> bool {\n    e.to_string().starts_with(\"failed to read segment header\")\n}","tryCatchPattern":"match open_segment(&path) {\n    Ok(seg) => seg,\n    Err(e) if is_header_read_failure(&e) && e.kind() == io::ErrorKind::UnexpectedEof => {\n        // crash remnant shorter than a header: quarantine and continue\n        std::fs::rename(&path, path.with_extension(\"quarantine\"))?;\n        open_segment(&path)?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep segment preallocation (fallocate) enabled for crash safety","Monitor for zero-length or tiny segment files after crashes and clean them before reopen","Validate backups by checking file sizes before restore"],"tags":["rust","commitlog","segment-header","unexpected-eof","truncated-file","io"],"backgroundTag":"truncated-file-read","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}