{"record":{"id":"1e1bfd58afe393d7","repo":"vectordotdev/vector","slug":"a-record-with-a-next-id-must-have-an-event-count","errorCode":null,"errorMessage":"a record with a next ID must have an event count","messagePattern":"a record with a next ID must have an event count","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"lib/vector-buffers/src/variants/disk_v2/checkpoint_recovery.rs","lineNumber":704,"sourceCode":"    /// Returns the truncation boundary before a record that straddles the writer checkpoint.\n    fn checkpoint_crossing_truncation_offset(\n        scan: &CheckpointBoundaryScan<'_>,\n        record: &CheckpointRecord,\n    ) -> Option<u64> {\n        let writer_next_record_id = scan.writer_next_record_id?;\n        let next_id = record.next_id?;\n        if next_id <= writer_next_record_id {\n            return None;\n        }\n\n        let truncate_at = scan.current_offset();\n        warn!(\n            data_file_id = scan.file.id,\n            writer_next_record_id,\n            record_id = record.id,\n            record_events = record\n                .events\n                .expect(\"a record with a next ID must have an event count\"),\n            truncate_at,\n            \"Record crosses writer checkpoint; truncating file.\"\n        );\n        Some(truncate_at)\n    }\n\n    /// Returns the physical file size, or `None` when the checkpointed file is missing.\n    async fn data_file_size(&self, data_file_path: &Path) -> Result<Option<u64>, ReaderError<T>> {\n        match self\n            .ledger()\n            .filesystem()\n            .open_file_readable(data_file_path)\n            .await\n        {\n            Ok(data_file) => data_file\n                .metadata()\n                .await\n                .map(|metadata| Some(metadata.len()))","sourceCodeStart":686,"sourceCodeEnd":722,"githubUrl":"https://github.com/vectordotdev/vector/blob/3708c39b12a93212ed8b8d7510b4cc7769cb5864/lib/vector-buffers/src/variants/disk_v2/checkpoint_recovery.rs#L686-L722","documentation":"While deciding where to truncate a data file whose records cross the writer checkpoint, recovery reads `record.next_id` and then expect()s the paired `events` field. Records are constructed so both are set together (the scanner returns early for undecodable/empty records and only sets next_id as id + events), so a panic here means the record-construction invariant was broken internally. No on-disk input constructs a record with next_id but no event count.","triggerScenarios":"A CheckpointRecord built with next_id set and events None, hit when a scanned record extends past writer_next_record_id during checkpoint recovery — reachable only through an internal Vector bug or memory corruption.","commonSituations":"Practically unseen; would appear in a crash report during disk_v2 buffer recovery after an unclean shutdown, on a state the scanner mis-built.","solutions":["Move the buffer data directory aside and restart to recreate it","If reproducible, capture RUST_BACKTRACE=1 output and file an upstream issue with the Vector version"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["No caller-side guard exists; the invariant is enforced by construction inside the scanner","Keep the move-aside-and-recreate recovery procedure handy for disk_v2 buffers"],"tags":["rust","vector","disk-buffer","checkpoint","invariant","panic"],"backgroundTag":"disk-buffer-checkpoint-corruption","analyzedSha":"3708c39b12a93212ed8b8d7510b4cc7769cb5864","analyzedAt":"2026-08-20T07:02:18.786Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T20:17:18.057Z"}