{"record":{"id":"51c22c129fa42f90","repo":"apache/cassandra","slug":"the-postings-position-is-less-than-zero","errorCode":null,"errorMessage":"The postings position is less than zero.","messagePattern":"The postings position is less than zero\\.","errorType":"exception","errorClass":"CorruptIndexException","httpStatus":null,"severity":"critical","filePath":"src/java/org/apache/cassandra/index/sai/disk/v1/segment/NumericIndexSegmentSearcher.java","lineNumber":66,"sourceCode":"{\n    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());\n\n    private final BlockBalancedTreeReader treeReader;\n    private final QueryEventListener.BalancedTreeEventListener perColumnEventListener;\n\n    NumericIndexSegmentSearcher(PrimaryKeyMap.Factory primaryKeyMapFactory,\n                                PerColumnIndexFiles perIndexFiles,\n                                SegmentMetadata segmentMetadata,\n                                StorageAttachedIndex index) throws IOException\n    {\n        super(primaryKeyMapFactory, perIndexFiles, segmentMetadata, index);\n\n        final long treePosition = metadata.getIndexRoot(IndexComponent.BALANCED_TREE);\n        if (treePosition < 0)\n            throw new CorruptIndexException(index.identifier().logMessage(\"The tree position is less than zero.\"), IndexComponent.BALANCED_TREE.name);\n        final long postingsPosition = metadata.getIndexRoot(IndexComponent.POSTING_LISTS);\n        if (postingsPosition < 0)\n            throw new CorruptIndexException(index.identifier().logMessage(\"The postings position is less than zero.\"), IndexComponent.BALANCED_TREE.name);\n\n        treeReader = new BlockBalancedTreeReader(index.identifier(),\n                                                 indexFiles.balancedTree(),\n                                                 treePosition,\n                                                 indexFiles.postingLists(),\n                                                 postingsPosition);\n        perColumnEventListener = (QueryEventListener.BalancedTreeEventListener)index.columnQueryMetrics();\n    }\n\n    @Override\n    public long indexFileCacheSize()\n    {\n        return treeReader.memoryUsage();\n    }\n\n    @Override\n    public KeyRangeIterator search(Expression exp, AbstractBounds<PartitionPosition> keyRange, QueryContext context) throws IOException\n    {","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/index/sai/disk/v1/segment/NumericIndexSegmentSearcher.java#L48-L84","documentation":"Thrown as a CorruptIndexException when the SAI segment metadata records a negative root position for the POSTING_LISTS component of a numeric index segment. Segment component positions are file offsets into the component files and must never be negative, so a negative value means the metadata was written corruptly or the segment file/metadata pairing is inconsistent. It deliberately names BALANCED_TREE in the component field even when the postings position is the offender, an upstream quirk.","triggerScenarios":"Opening a numeric SAI segment whose SegmentMetadata contains IndexComponent.POSTING_LISTS root < 0, e.g. after a corrupted flush, truncated SSTable components, or manually edited/copied index files.","commonSituations":"Disk corruption or incomplete flush during crash, restoring index files from a backup taken mid-flush, mixing segment files from different generations, or running a Cassandra version against index files written by an incompatible one.","solutions":["Run nodetool scrubs/rebuild of the affected SSTables (or REBUILD the secondary index) so the segment is rewritten with valid metadata","Validate the segment file and metadata pairing (matching generation numbers) and restore from a good backup if files were moved manually","Check the storage medium/filesystem for corruption (dmesg, fsck)","If reproducible, file a bug with the segment metadata contents; the check should report the component it validated"],"exampleFix":"// before\nif (postingsPosition < 0)\n    throw new CorruptIndexException(index.identifier().logMessage(\"The postings position is less than zero.\"), IndexComponent.BALANCED_TREE.name);\n// after\nif (postingsPosition < 0)\n    throw new CorruptIndexException(index.identifier().logMessage(\"The postings position is less than zero.\"), IndexComponent.POSTING_LISTS.name);","handlingStrategy":"validation","validationCode":"SegmentMetadata meta = readSegmentMetadata(...);\nif (meta.getIndexRoot(IndexComponent.POSTING_LISTS) < 0 || meta.getIndexRoot(IndexComponent.BALANCED_TREE) < 0)\n    throw new CorruptIndexException(\"negative component root in \" + index.identifier(), \"segment\");","typeGuard":"boolean hasValidRoots(SegmentMetadata m) {\n    return m.getIndexRoot(IndexComponent.BALANCED_TREE) >= 0 && m.getIndexRoot(IndexComponent.POSTING_LISTS) >= 0;\n}","tryCatchPattern":null,"preventionTips":["Never move or copy individual index component files; treat a segment's components as an atomic set","Run scrub/rebuild after suspected crashes before reopening indexes","Monitor for CorruptIndexException in logs and rebuild indexes proactively","Keep node versions consistent across the cluster"],"tags":["cassandra","sai","index-corruption","storage"],"backgroundTag":"checksum-mismatch","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}