apache/cassandra · critical · IOException

Corrupted sstable. Invalid flags found deserializing Deletio

Error message

Corrupted sstable. Invalid flags found deserializing DeletionTime: " + Integer.toBinaryString(flags & 0xFF)

What it means

DeletionTime.Serializer.deserialize(DataInputPlus) reads a one-byte flags field from the wire. The top bit (IS_LIVE_DELETION) marks a live (non-deleted) DeletionTime; if it is set but any of the other 7 bits are also set, the encoding is not a valid live marker, so the deserializer throws an IOException declaring the sstable corrupted. This guards against reading data written by incompatible versions or truncated/corrupted files.

Source

Thrown at src/java/org/apache/cassandra/db/DeletionTime.java:256

        public void serialize(DeletionTime delTime, DataOutputPlus out) throws IOException
        {
            if (delTime == LIVE || delTime.isLive())
                out.writeByte(IS_LIVE_DELETION);
            else
            {
                // The sign bit is zero here, so we can write a long directly
                out.writeLong(delTime.markedForDeleteAt());
                out.writeInt(delTime.localDeletionTimeUnsignedInteger);
            }
        }

        public DeletionTime deserialize(DataInputPlus in) throws IOException
        {
            int flags = in.readByte();
            if ((flags & IS_LIVE_DELETION) != 0)
            {
                if ((flags & 0xFF) != IS_LIVE_DELETION)
                    throw new IOException("Corrupted sstable. Invalid flags found deserializing DeletionTime: " + Integer.toBinaryString(flags & 0xFF));
                return LIVE;
            }
            else
            {
                // Read the remaining 7 bytes
                int bytes1 = in.readByte();
                int bytes2 = in.readShort();
                int bytes4 = in.readInt();

                long mfda = readBytesToMFDA(flags, bytes1, bytes2, bytes4);
                int localDeletionTimeUnsignedInteger = in.readInt();

                return new ImmutableDeletionTime(mfda, localDeletionTimeUnsignedInteger);
            }
        }

        public void deserialize(DataInputPlus in, ReusableDeletionTime reuse) throws IOException
        {

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Run `nodetool scrub` (or `sstablescrub`) on the affected table to repair or drop the corrupted sstable.
  2. Verify disk/RAM health (smartctl, fsck) and restore the affected sstables from a backup or by rebuilding via repair (`nodetool repair`) from replicas.
  3. Check that all nodes run compatible Cassandra versions and that no partial writes (e.g. from a crash during compaction) remain; delete leftover temp sstables.
  4. Reproduce with the DeletionTime serializer unit tests to confirm the flags encoding on your build is 0x80 for live deletions.

Example fix

// before: blindly deserializing a possibly-corrupt stream
DeletionTime dt = DeletionTime.serializer.deserialize(in);
// after: validate flags yourself and fail fast with context
int flags = in.readByte();
if ((flags & 0x80) != 0 && (flags & 0xFF) != 0x80)
    throw new CorruptSSTableException(new IOException("Bad DeletionTime flags: " + Integer.toBinaryString(flags & 0xFF)), file);
Defensive patterns

Strategy: try-catch

Validate before calling

// before deserializing from an untrusted file, validate sstable integrity
File sstable = new File(path);
if (!Digest.validate(sstable, digestFile)) throw new CorruptSSTableException(...);

Type guard

// check the flags byte yourself before full deserialize
static boolean isValidDeletionTimeFlags(int flags) {
    return (flags & 0x80) == 0 || (flags & 0xFF) == 0x80;
}

Try / catch

try {
    DeletionTime dt = DeletionTime.serializer.deserialize(in);
} catch (IOException e) {
    if (e.getMessage().contains("Invalid flags found deserializing DeletionTime"))
        throw new CorruptSSTableException(e, sstableFile); // trigger repair/scrub
    throw e;
}

Prevention

When it happens

Trigger: Reading a Memtable/SSTable-backed partition or an index entry (IndexedEntry), or test/testE2EDeSerializeDT deserialization paths, where the byte read at the DeletionTime position has bit 0x80 set together with other bits (e.g. flags != 0x80). Caused by byte-stream misalignment, corruption, or a writer producing a malformed encoding.

Common situations: Bit-flip disk corruption or truncation of SSTable files; hand-crafted or corrupted commitlog/memtable data; reading sstables produced by a broken build or different Cassandra version with a changed on-wire format; failed reads surfaced via CorruptSSTableException wrappers in logs.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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