apache/cassandra · error · MarshalException

A local deletion time should not be negative

Error message

A local deletion time should not be negative

What it means

AbstractCell.validate() also requires the cell's localDeletionTime (the wall-clock time at which an expiring cell becomes a tombstone) to be non-negative. Negative values are impossible from legitimate writers and indicate corrupt or malformed data, so validation fails with MarshalException.

Source

Thrown at src/java/org/apache/cassandra/db/rows/AbstractCell.java:191

    {
        if (isCounterCell())
            digest.updateWithCounterContext(value(), accessor());
        else
            digest.update(value(), accessor());

        digest.updateWithLong(timestamp())
              .updateWithInt(ttl())
              .updateWithBoolean(isCounterCell());
        if (path() != null)
            path().digest(digest);
    }

    public void validate()
    {
        if (ttl() < 0)
            throw new MarshalException("A TTL should not be negative");
        if (localDeletionTime() < 0)
            throw new MarshalException("A local deletion time should not be negative");
        if (localDeletionTime() == INVALID_DELETION_TIME)
            throw new MarshalException("A local deletion time should not be a legacy overflowed value");
        if (isExpiring() && localDeletionTime() == NO_DELETION_TIME)
            throw new MarshalException("Shoud not have a TTL without an associated local deletion time");

        // non-frozen UDTs require both the cell path & value to validate,
        // so that logic is pushed down into ColumnMetadata. Tombstone
        // validation is done there too as it also involves the cell path
        // for complex columns
        column().validateCell(this);
    }

    public boolean hasInvalidDeletions()
    {
        if (ttl() < 0 || localDeletionTime() == INVALID_DELETION_TIME || localDeletionTime() < 0 || (isExpiring() && localDeletionTime() == NO_DELETION_TIME))
            return true;
        return false;
    }

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Run `nodetool scrub` on the affected table to remove invalid cells.
  2. Restore affected SSTables from backup.
  3. Check storage medium for corruption.
  4. Verify that no custom tooling writes cells directly with negative deletion times.

Example fix

// before
int ldt = readDeletionTime(buf); // may be negative
// after
int ldt = readDeletionTime(buf);
if (ldt < 0) throw new IOException("corrupt local deletion time: " + ldt);
Defensive patterns

Strategy: validation

Validate before calling

if (cell.localDeletionTime() < 0) throw new IllegalArgumentException("negative localDeletionTime before write");

Type guard

boolean hasValidDeletionTime(AbstractCell<?> c) { return c.localDeletionTime() >= 0; }

Try / catch

try { row.validate(); } catch (MarshalException e) { quarantineAndScrub(metadata.keyspace, metadata.name, e); }

Prevention

When it happens

Trigger: Reading a cell with a corrupted localDeletionTime field from an SSTable/commitlog; deserializing tampered or wrongly encoded data; buggy custom serializers producing negative deletion times.

Common situations: Bit-rot on storage; incompatible or third-party SSTable writers; malformed streamed data from a misbehaving node.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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