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
- Run `nodetool scrub` on the affected table to remove invalid cells.
- Restore affected SSTables from backup.
- Check storage medium for corruption.
- 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
- Clamp deletion times to >= 0 in any custom serializer
- Restore from trusted backups only
- Monitor storage for bit rot
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
- A local deletion time should not be negative in '%s'
- A TTL should not be negative
- A local deletion time should not be a legacy overflowed valu
- Shoud not have a TTL without an associated local deletion ti
- A TTL should not be negative
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/46a0d3982b21f6eb.
Report an issue: GitHub.