{"record":{"id":"f9c0cc29a695f755","repo":"apache/cassandra","slug":"shoud-not-have-a-ttl-without-an-associated-local-d","errorCode":null,"errorMessage":"Shoud not have a TTL without an associated local deletion time","messagePattern":"Shoud not have a TTL without an associated local deletion time","errorType":"validation","errorClass":"MarshalException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/db/rows/AbstractCell.java","lineNumber":195,"sourceCode":"            digest.update(value(), accessor());\n\n        digest.updateWithLong(timestamp())\n              .updateWithInt(ttl())\n              .updateWithBoolean(isCounterCell());\n        if (path() != null)\n            path().digest(digest);\n    }\n\n    public void validate()\n    {\n        if (ttl() < 0)\n            throw new MarshalException(\"A TTL should not be negative\");\n        if (localDeletionTime() < 0)\n            throw new MarshalException(\"A local deletion time should not be negative\");\n        if (localDeletionTime() == INVALID_DELETION_TIME)\n            throw new MarshalException(\"A local deletion time should not be a legacy overflowed value\");\n        if (isExpiring() && localDeletionTime() == NO_DELETION_TIME)\n            throw new MarshalException(\"Shoud not have a TTL without an associated local deletion time\");\n\n        // non-frozen UDTs require both the cell path & value to validate,\n        // so that logic is pushed down into ColumnMetadata. Tombstone\n        // validation is done there too as it also involves the cell path\n        // for complex columns\n        column().validateCell(this);\n    }\n\n    public boolean hasInvalidDeletions()\n    {\n        if (ttl() < 0 || localDeletionTime() == INVALID_DELETION_TIME || localDeletionTime() < 0 || (isExpiring() && localDeletionTime() == NO_DELETION_TIME))\n            return true;\n        return false;\n    }\n\n    public long maxTimestamp()\n    {\n        return timestamp();","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/db/rows/AbstractCell.java#L177-L213","documentation":"An expiring cell (a cell with a positive TTL) must also carry a valid localDeletionTime, since that timestamp determines when it expires into a tombstone. A TTL without localDeletionTime makes expiry undefined, so validate() rejects the cell with MarshalException. Note the message contains a typo (\"Shoud\") in the source.","triggerScenarios":"Deserializing a cell where ttl > 0 but localDeletionTime == NO_DELETION_TIME (corruption or buggy serializer); custom cell factories constructing expiring cells without setting the deletion time.","commonSituations":"Hand-written or third-party SSTable writers forgetting to set localDeletionTime; corrupted rows after disk issues; incorrectly implemented streaming/paxos payloads.","solutions":["Always set localDeletionTime together with TTL: use (now + ttl) in seconds when building cells.","Scrub affected SSTables to drop invalid expiring cells.","Restore affected data from backup.","Fix any custom serializer so ttl<=0 implies NO_DELETION_TIME and ttl>0 implies a real deletion time."],"exampleFix":"// before\ncell = factory.cell(col, ts, ttl, NO_DELETION_TIME, value, path);\n// after\ncell = factory.cell(col, ts, ttl, ttl > 0 ? (int)(nowSec + ttl) : NO_DELETION_TIME, value, path);","handlingStrategy":"validation","validationCode":"if (ttl > 0 && ldt == NO_DELETION_TIME) throw new IllegalArgumentException(\"TTL requires localDeletionTime\");","typeGuard":"boolean hasConsistentExpiry(AbstractCell<?> c) { return !c.isExpiring() || c.localDeletionTime() != NO_DELETION_TIME; }","tryCatchPattern":"try { row.validate(); } catch (MarshalException e) { quarantineCorruptRow(e); }","preventionTips":["When constructing expiring cells always compute ldt = nowSeconds + ttl","Unit-test custom cell factories against validate()","Never persist cells built from partial deserialization"],"tags":["validation","ttl","tombstone","data-corruption"],"backgroundTag":"schema-validation-failed","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}