apache/cassandra · error · IOException

Clustering block upper bits (those not associated with keys)

Error message

Clustering block upper bits (those not associated with keys) expected to be 0:${clusteringBlockHeader}

What it means

While deserializing clustering values, SSTableCursorReader consumes a bit-per-key clustering block header. After shifting through all keys, the remaining upper bits must be zero; nonzero residual bits mean the header encodes keys not covered by the schema, so the data is treated as corrupt and an IOException is thrown.

Source

Thrown at src/java/org/apache/cassandra/io/sstable/SSTableCursorReader.java:1056

            //    varint clustering_block_header;
            //    simple_cell[] clustering_cells;
            // };
            if (clusteringIndex % 32 == 0)
            {
                fixedLengthClusteringLength = flushFixedLengthRun(dataReader, clustering, fixedLengthClusteringLength);
                clusteringBlockHeader = dataReader.readUnsignedVInt();
                clustering.writeUnsignedVInt(clusteringBlockHeader);
            }

            // load value if present
            if ((clusteringBlockHeader & 0b11) == 0)
                fixedLengthClusteringLength = readClusteringValue(dataReader, clustering, types[clusteringIndex], fixedLengthClusteringLength);

            clusteringBlockHeader = clusteringBlockHeader >>> 2;
        }
        flushFixedLengthRun(dataReader, clustering, fixedLengthClusteringLength);
        if (clusteringBlockHeader != 0) {
            throw new IOException("Clustering block upper bits (those not associated with keys) expected to be 0:" + clusteringBlockHeader);
        }
    }

    /**
     * Copies the pending run of fixed-length components in one read. They are adjacent on disk, so
     * the run is only broken by a variable-length component or by a block boundary.
     *
     * @return the new pending run length, always 0
     */
    private static int flushFixedLengthRun(RandomAccessReader dataReader, ResizableByteBuffer clustering, int fixedLengthClusteringLength) throws IOException
    {
        if (fixedLengthClusteringLength != 0)
            clustering.loadPart(dataReader, fixedLengthClusteringLength);
        return 0;
    }

    /**
     * Reads one present clustering component. A fixed-length component joins the pending run; a

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Run nodetool verify on the sstable; scrub if corrupt.
  2. Ensure schema agreement across the cluster (nodetool describecluster) and propagate schema changes.
  3. Restore the sstable from backup or rebuild via nodetool repair if corruption is confirmed.
  4. Confirm the sstable was not copied from a table with a different clustering definition.

Example fix

// before
Row row = cursor.readRow(); // raw IOException
// after
try {
    Row row = cursor.readRow();
} catch (IOException e) {
    throw new CorruptSSTableException(e, ssTableReader.getFilename());
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    row = cursor.readRow();
} catch (IOException e) {
    throw new CorruptSSTableException(e, filename); // caller triggers scrub
}

Prevention

When it happens

Trigger: Reading a row whose clustering block header has bits set beyond the number of clustering columns/types known from the schema — schema mismatch (table altered) or corrupted bytes.

Common situations: Reading sstables written under an incompatible schema; nodes with stale schema (missed schema changes); bit-flip disk corruption.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/6653419ea00b9caf. Report an issue: GitHub.