apache/cassandra · error · MarshalException

A local deletion time should not be negative in '%s'

Error message

A local deletion time should not be negative in '%s'

What it means

AbstractRow.validateData validates a full row against table metadata. The row-level deletion (tombstone) time must be non-negative; a negative localDeletionTime in the row's DeletionTime means corrupted or malformed data, rejected with MarshalException including the table name.

Source

Thrown at src/java/org/apache/cassandra/db/rows/AbstractRow.java:89

                try
                {
                    metadata.comparator.subtype(i).validate(value, accessor);
                }
                catch (Exception e)
                {
                    throw new MarshalException("comparator #" + i + " '" + metadata.comparator.subtype(i) + "' in '" + metadata + "' didn't validate", e);
                }
            }
        }
    }

    public void validateData(TableMetadata metadata)
    {
        validateClustering(metadata, clustering());

        primaryKeyLivenessInfo().validate();
        if (deletion().time().localDeletionTime() < 0)
            throw new MarshalException("A local deletion time should not be negative in '" + metadata + "'");

        apply(cd -> cd.validate());
    }

    public boolean hasInvalidDeletions()
    {
        if (primaryKeyLivenessInfo().isExpiring() && (primaryKeyLivenessInfo().ttl() < 0 || primaryKeyLivenessInfo().localExpirationTime() < 0))
            return true;
        if (!deletion().time().validate())
            return true;
        for (ColumnData cd : this)
            if (cd.hasInvalidDeletions())
                return true;
        return false;
    }

    public String toString()
    {

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Run `nodetool scrub` on the affected table to drop invalid rows.
  2. Restore affected data files from backup.
  3. Check hardware/filesystem for corruption.
  4. Fix any custom loader/serializer so tombstone localDeletionTime is always >= 0 (use NO_DELETION_TIME = Int.MAX_VALUE when there is no deletion).

Example fix

// before
DeletionTime dt = new DeletionTime(ts, negativeLdt);
// after
if (negativeLdt < 0) throw new IllegalArgumentException("invalid localDeletionTime");
DeletionTime dt = new DeletionTime(ts, negativeLdt);
Defensive patterns

Strategy: validation

Validate before calling

if (row.deletion().time().localDeletionTime() < 0) throw new IllegalArgumentException("invalid row tombstone");

Try / catch

try { row.validateData(metadata); } catch (MarshalException e) { scrubTable(metadata.keyspace, metadata.name); }

Prevention

When it happens

Trigger: Deserializing a row whose row-tombstone deletion time bytes are corrupted; reading SSTables/commitlog/stream sections written by a buggy encoder; manual data import with malformed tombstones.

Common situations: Disk corruption; third-party bulk loaders writing invalid tombstones; streaming from a node with corrupt data.

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/5418a47f6fdb8d7e. Report an issue: GitHub.