{"record":{"id":"22cf728445c8a579","repo":"apache/cassandra","slug":"invalid-block-offset-d-for-postings-block-idx-d","errorCode":null,"errorMessage":"Invalid block offset %d for postings block idx %d","messagePattern":"Invalid block offset (.+?) for postings block idx (.+?)","errorType":"exception","errorClass":"CorruptIndexException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/index/sai/disk/v1/postings/PostingsReader.java","lineNumber":328,"sourceCode":"        long id = currentFoRValues.get(postingIndex);\n        postingsDecoded++;\n        return Math.toIntExact(id);\n    }\n\n    private void advanceOnePosition(long nextPosting)\n    {\n        actualPosting = nextPosting;\n        totalPostingsRead++;\n        postingIndex++;\n    }\n\n    private void reBuffer() throws IOException\n    {\n        long pointer = summary.offsets.get(blockIndex);\n        if (pointer < 4)\n        {\n            // the first 4 bytes must be CODEC_MAGIC\n            throw new CorruptIndexException(String.format(\"Invalid block offset %d for postings block idx %d\", pointer, blockIndex), input);\n        }\n        input.seek(pointer);\n\n        long left = summary.numPostings - totalPostingsRead;\n        assert left > 0;\n\n        readFoRBlock(input);\n\n        blockIndex++;\n        postingIndex = 0;\n    }\n\n    private void readFoRBlock(IndexInput in) throws IOException\n    {\n        if (blockIndex == 0)\n            actualPosting = in.readVLong();\n\n        byte bitsPerValue = in.readByte();","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/index/sai/disk/v1/postings/PostingsReader.java#L310-L346","documentation":"PostingsReader.reBuffer() seeks to the block offset stored in the postings summary. Offsets < 4 are invalid because the first 4 bytes of an index component file must be CodecUtil's CODEC_MAGIC, so any valid block must start at or after byte 4. Hitting this means the summary offsets array is corrupt or the wrong component file is attached, and it is thrown as Lucene CorruptIndexException.","triggerScenarios":"Advancing the postings iterator into a block whose summary.offsets.get(blockIndex) is < 4 (typically 0 or negative from an unwritten/truncated offsets entry).","commonSituations":"Truncated or zero-filled SAI postings files after a crash or disk failure, component file mix-ups (reading one component with another's summary), or bit-rot.","solutions":["Rebuild the SAI index to regenerate postings and summary components.","Verify checksums of the IndexComponent files and restore from a healthy snapshot.","Ensure the PostingsReader is opened with the matching summary for the same posting list (correct component/segment)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"long off = summary.offsets.get(blockIndex); if (off < 4) failFast(); // pre-check before reader advances","typeGuard":null,"tryCatchPattern":"try { iterator.next(); } catch (CorruptIndexException e) { log.error(\"Corrupt postings\", e); markIndexCorrupt(index); }","preventionTips":["Validate index component checksums on open.","Restore from snapshot if files are truncated; rebuild index if corruption recurs."],"tags":["cassandra","sai","index-corruption","postings"],"backgroundTag":"checksum-mismatch","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}