{"record":{"id":"b3606c3dfed94f91","repo":"astrid-runtime/astrid","slug":"invalid-astrid-volume-record-length-at-offset","errorCode":null,"errorMessage":"invalid Astrid volume record length at {offset}","messagePattern":"invalid Astrid volume record length at (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage/src/volume/hosted/recover.rs","lineNumber":423,"sourceCode":"fn handle_bad_length(\n    file: &File,\n    offset: u64,\n    physical_len: u64,\n    total_len: u64,\n    remaining: u64,\n) -> io::Result<Option<RecordHeader>> {\n    if total_len > remaining\n        && has_physically_valid_record_after(file, offset.saturating_add(1), physical_len)?\n    {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"invalid interior Astrid volume record length at {offset}\"),\n        ));\n    }\n    if total_len > remaining {\n        return Ok(None);\n    }\n    Err(io::Error::new(\n        io::ErrorKind::InvalidData,\n        format!(\"invalid Astrid volume record length at {offset}\"),\n    ))\n}\n\nfn read_record_payload(file: &File, header: &RecordHeader) -> io::Result<Option<Vec<u8>>> {\n    if header.operation == Operation::Write {\n        return Ok(None);\n    }\n    let max_payload = if header.operation == Operation::Commit {\n        MAX_COMMIT_SNAPSHOT_BYTES\n    } else {\n        MAX_METADATA_PAYLOAD_BYTES\n    };\n    if header.payload_len > max_payload {\n        return Err(invalid_transition(\n            \"Astrid volume metadata payload exceeds the recovery bound\",\n        ));","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage/src/volume/hosted/recover.rs#L405-L441","documentation":"A record header claims a total_len greater than the bytes remaining in the region, and no physically valid record follows it, so recovery cannot interpret the tail as another record. Because this is the final bad-length record (no valid successor found), recovery reports plain 'invalid record length' InvalidData for that offset.","triggerScenarios":"handle_bad_length (from read_header) finds total_len > remaining and has_physically_valid_record_after(offset+1) is false — the tail of the volume contains a header whose length cannot fit, with no resynchronizable record after it (e.g. torn final write).","commonSituations":"The process crashed mid-append leaving a partial record header at the tail; the volume file was truncated (copied incompletely, disk full during write); a caller recovers a file captured mid-write.","solutions":["Check whether the volume file is complete (size matches what the writer expected); restore or re-sync an incompletely copied/truncated file.","Recover from backup or the last committed footer/commit point.","If only the tail record is affected and the region tolerates losing the last write, discard the torn tail and re-open the volume.","Investigate why the writer was interrupted (OOM, disk-full, kill -9) before reusing the volume."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// cheap pre-check: ensure expected file size is present\nlet meta = file.metadata()?;\nif meta.len() < expected_min_volume_size { return Err(\"volume file truncated\"); }","typeGuard":null,"tryCatchPattern":"match recover_from_headers(&file) {\n    Err(e) if e.kind() == io::ErrorKind::InvalidData && e.to_string().contains(\"invalid Astrid volume record length\") => {\n        // treat as torn tail: recover to last good record or restore backup\n    }\n    other => other?,\n}","preventionTips":["Use atomic/append-safe shutdown paths; avoid kill -9 during writes.","Ensure adequate disk space so appends never complete partially.","Verify file integrity (size/checksum) after copying volumes between hosts.","Restore from backup rather than replaying a truncated tail when in doubt."],"tags":["storage","io","truncation","recovery"],"backgroundTag":"file-read-failed","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}