apache/cassandra · error · MarshalException

A local expiration time should not be negative

Error message

A local expiration time should not be negative

What it means

Same validator as the TTL check: ExpiringLivenessInfo.validate() rejects a negative localExpirationTime, the wall-clock expiration timestamp computed as now + TTL. A negative value is impossible for valid expiration data, so MarshalException is thrown to keep corrupt values out of the storage engine.

Source

Thrown at src/java/org/apache/cassandra/db/LivenessInfo.java:336

        @Override
        public void digest(Digest digest)
        {
            super.digest(digest);

            // As of 5.0, local expiration times are encoded as unsigned integers on disk, so we can do the
            // same thing here to populate the digest. This supports extended TTLs, but also maintains digest
            // compatibility with previous versions, avoiding false digest mismatches during upgrades.
            digest.updateWithInt(Cell.deletionTimeLongToUnsignedInteger(localExpirationTime));
            digest.updateWithInt(ttl);
        }

        @Override
        public void validate()
        {
            if (ttl < 0)
                throw new MarshalException("A TTL should not be negative");
            if (localExpirationTime < 0)
                throw new MarshalException("A local expiration time should not be negative");
        }

        @Override
        public int dataSize()
        {
            return super.dataSize()
                 + TypeSizes.sizeof(ttl)
                 + TypeSizes.sizeof(localExpirationTime);

        }

        @Override
        public LivenessInfo withUpdatedTimestamp(long newTimestamp)
        {
            return new ExpiringLivenessInfo(newTimestamp, ttl, localExpirationTime);
        }

        @Override

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Run `nodetool scrub` on affected tables if negative values come from stored data.
  2. Fix any code that constructs expiration times to compute now + ttl with non-negative inputs.
  3. Verify system clocks (NTP) on nodes writing expiration data.

Example fix

// before
ExpiringLivenessInfo exp = new ExpiringLivenessInfo(ttl, localExpirationTime, nowInSeconds); // localExpirationTime < 0 possible
// after
if (localExpirationTime < 0) throw new IllegalArgumentException("invalid expiration");
ExpiringLivenessInfo exp = new ExpiringLivenessInfo(ttl, localExpirationTime, nowInSeconds);
Defensive patterns

Strategy: validation

Validate before calling

if (localExpirationTime < 0) throw new IllegalArgumentException("localExpirationTime must be >= 0");

Type guard

long safeExpiration(long ttl, long now) { return Math.max(0, now + Math.max(0, ttl)); }

Try / catch

try { readOrWrite(); } catch (MarshalException e) { if (e.getMessage().contains("expiration")) { triggerScrubAndAlert(); } else throw e; }

Prevention

When it happens

Trigger: Deserializing a cell whose localExpirationTime field is negative (corrupt SSTable, bad internode message); constructing ExpiringLivenessInfo directly with a negative expiration; extreme clock manipulation producing negative epoch values.

Common situations: Disk corruption or failed compaction leftovers; hand-rolled serialization in tools/tests; system clock set before epoch in exotic environments.

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