{"record":{"id":"ff6b487df842b66c","repo":"apache/cassandra","slug":"corrupt-negative-clustering-value-length-encount","errorCode":null,"errorMessage":"Corrupt (negative) clustering value length encountered: ${length}","messagePattern":"Corrupt \\(negative\\) clustering value length encountered: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/io/sstable/SSTableCursorReader.java","lineNumber":1105,"sourceCode":"        validateClusteringValueLength(varLength);\n        clustering.writeUnsignedVInt(varLength);\n        clustering.loadPart(dataReader, varLength);\n        return 0;\n    }\n\n    /**\n     * Rejects a clustering value length the wire cannot have produced honestly. Both checks mirror\n     * AbstractType.read, the reference for this format. readUnsignedVInt32 can return a negative\n     * int, which is why the first check exists: an unchecked negative length reaches\n     * {@link java.io.DataInput#skipBytes} as a silent no-op, and a buffer sizer as a defect.\n     *\n     * <p>Every caller of this walk wraps it and reports a {@code CorruptSSTableException}.\n     */\n    @VisibleForTesting\n    static void validateClusteringValueLength(int length) throws IOException\n    {\n        if (length < 0)\n            throw new IOException(\"Corrupt (negative) clustering value length encountered: \" + length);\n        if (length > DatabaseDescriptor.getMaxValueSize())\n            throw new IOException(String.format(\"Corrupt clustering value length %d encountered, as it exceeds the maximum of %d, \" +\n                                                \"which is set via max_value_size in cassandra.yaml\",\n                                                length, DatabaseDescriptor.getMaxValueSize()));\n    }\n\n    private static void skipClustering(RandomAccessReader dataReader, AbstractType<?>[] types, int clusteringColumnsBound) throws IOException\n    {\n        long clusteringBlockHeader = 0;\n        for (int clusteringIndex = 0; clusteringIndex < clusteringColumnsBound; clusteringIndex++)\n        {\n            // struct clustering_block {\n            //    varint clustering_block_header;\n            //    simple_cell[] clustering_cells;\n            // };\n            if (clusteringIndex % 32 == 0)\n            {\n                clusteringBlockHeader = dataReader.readUnsignedVInt();","sourceCodeStart":1087,"sourceCodeEnd":1123,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/io/sstable/SSTableCursorReader.java#L1087-L1123","documentation":"validateClusteringValueLength rejects a negative length read from disk for a clustering value. A negative length cannot be a valid encoded size, so the bytes are corrupt and an IOException is thrown; callers wrap it in CorruptSSTableException.","triggerScenarios":"Deserializing/skipping clustering values where the on-disk length prefix decodes to a negative int — corrupted sstable bytes or misaligned reads following earlier corruption.","commonSituations":"Disk/bit-flip corruption; sstables truncated or written by a buggy writer; cursor misalignment after a prior deserialization bug.","solutions":["Run nodetool verify / sstableverify to confirm corruption.","Run nodetool scrub (sstablescrub) to salvage or quarantine the affected sstable.","Restore the file from backup and run nodetool repair to restore replication consistency.","If reads are misaligned, fix the preceding deserialization code so offsets stay correct."],"exampleFix":"// before\nint len = in.readUnsignedShort();\nskipClustering(in, types, len);\n// after\nint len = in.readUnsignedShort();\nvalidateClusteringValueLength(len); // throws on negative/oversized before use\nskipClustering(in, types, len);","handlingStrategy":"try-catch","validationCode":"if (length < 0) throw new IOException(\"negative clustering length: \" + length);","typeGuard":null,"tryCatchPattern":"try {\n    value = readClusteringValue(in);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"Corrupt\"))\n        throw new CorruptSSTableException(e, filename);\n    throw e;\n}","preventionTips":["Monitor disks (SMART/dmesg) for hardware corruption sources.","Verify sstables after unclean shutdowns.","Use digest-verified binary copies when moving sstables.","Scrub sstables flagged by verify promptly."],"tags":["sstable","corruption","deserialization"],"backgroundTag":"invalid-argument-value","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"}