{"record":{"id":"31153ee76bdc6d7a","repo":"apache/cassandra","slug":"invalid-columns-subset-bytes-too-many-bits-set","errorCode":null,"errorMessage":"Invalid Columns subset bytes; too many bits set: ${encoded}","messagePattern":"Invalid Columns subset bytes; too many bits set: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/io/sstable/UnfilteredDescriptor.java","lineNumber":174,"sourceCode":"        if (UnfilteredSerializer.hasAllColumns(flags))\n            missingColumnsMask = 0;\n        else if (rowColumns.size() < 64)\n            readSmallColumnsSubset(dataReader);\n        else\n            readLargeColumnsSubset(dataReader);\n    }\n\n    /**\n     * Columns.Serializer.deserializeSubset would build a Columns per row, so decode its wire format\n     * here: an unsigned vint bitmask of the missing superset columns. rowColumns stays the superset,\n     * and consumers filter with missingColumnsMask().\n     */\n    private void readSmallColumnsSubset(RandomAccessReader dataReader) throws IOException\n    {\n        long encoded = dataReader.readUnsignedVInt();\n        // Mirrors the corruption check in Columns.Serializer.deserializeSubset.\n        if ((encoded >>> rowColumns.size()) != 0)\n            throw new IOException(\"Invalid Columns subset bytes; too many bits set: \" + Long.toBinaryString(encoded));\n        missingColumnsMask = encoded;\n    }\n\n    /**\n     * Wire format per Columns.Serializer.serializeLargeSubset: an unsigned vint delta of\n     * supersetCount - presentCount, then one unsigned vint superset index per column. The indices\n     * name the present columns when presentCount is under half the superset, and the missing\n     * columns otherwise. Decoding into reusable mask words leaves rowColumns as the superset, so\n     * CellCursor never rebuilds its per-superset arrays.\n     */\n    private void readLargeColumnsSubset(RandomAccessReader dataReader) throws IOException\n    {\n        long encoded = dataReader.readUnsignedVInt();\n        int supersetCount = rowColumns.size();\n        if (encoded > supersetCount)\n            throw new IOException(\"Invalid large Columns subset: missing count \" + encoded + \" of \" + supersetCount);\n\n        int delta = (int) encoded;","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/io/sstable/UnfilteredDescriptor.java#L156-L192","documentation":"UnfilteredDescriptor.readSmallColumnsSubset decodes a 'small' Columns subset from the sstable UnfilteredDescriptor block: a bitmask stored as an unsigned vint where each set bit refers to a column index in the row's column superset. If any bit beyond rowColumns.size() is set the encoding is impossible, so the reader throws IOException ('Invalid Columns subset bytes; too many bits set') indicating corrupt or miswritten sstable data (src/java/org/apache/cassandra/io/sstable/UnfilteredDescriptor.java:174), mirroring Columns.Serializer.deserializeSubset's corruption check.","triggerScenarios":"Reading an sstable (loadCommonRowFields path) whose descriptor block contains a small-subset mask with high bits set beyond the column count — i.e. the bytes at that offset are not a valid subset mask: bit-flip disk corruption, a truncated/misaligned read at the wrong offset, or a file written by an incompatible writer.","commonSituations":"Disk corruption or failing hardware on an sstable; copying/moving sstable files without snapshots leading to partial data; restoring a backup at the wrong position/offset; version mismatch where a reader misinterprets a differently encoded block from another Cassandra version.","solutions":["Run nodetool verify (or scrub) on the table to identify and quarantine the corrupt sstable","Restore the affected sstable(s) from a backup/snapshot or run an anticompaaction/repair to rebuild the data from other replicas","Check disk health (SMART, dmesg) for bit-rot and repair or replace the failing device","Ensure the reader and writer Cassandra versions use the same sstable format; upgrade the reader if the file uses a newer format"],"exampleFix":"// before\n// silently trusting on-disk bytes\ncolumns = Columns.Serializer.deserializeSubset(...);\n// after\nif ((encoded >>> supersetCount) != 0)\n    throw new IOException(\"Invalid Columns subset bytes; too many bits set: \" + Long.toBinaryString(encoded)); // corrupt sstable -> scrub/repair","handlingStrategy":"try-catch","validationCode":"long encoded = dataReader.readUnsignedVInt();\nif ((encoded >>> rowColumns.size()) != 0)\n    throw new IOException(\"corrupt small columns subset: \" + Long.toBinaryString(encoded));","typeGuard":null,"tryCatchPattern":"try {\n    readSmallColumnsSubset(reader);\n} catch (IOException e) {\n    // quarantine sstable and fall back to another replica / backup\n}","preventionTips":["Schedule regular nodetool verify to catch corrupt descriptors early","Use snapshots for all sstable movement; avoid manual partial copies","Replace hardware showing repeated single-bit corruption patterns"],"tags":["cassandra","sstable","corruption","deserialization"],"backgroundTag":"corrupt-sstable-data","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"}