apache/cassandra · error · InvalidRequestException
A TTL must be greater or equal to 0, but was <ttl>
Error message
A TTL must be greater or equal to 0, but was <ttl>
What it means
A bound TTL that decodes to a negative int is rejected: TTL semantics require 0 (no expiry) or a positive number of seconds. Attributes.getTimeToLive throws InvalidRequestException 'A TTL must be greater or equal to 0, but was <ttl>'.
Source
Thrown at src/java/org/apache/cassandra/cql3/Attributes.java:140
return metadata.params.defaultTimeToLive;
// byte[0] and null are the same for Int32Type. UNSET_BYTE_BUFFER is also byte[0] but we rely on pointer
// identity, so need to check this after checking that
if (ByteBufferUtil.EMPTY_BYTE_BUFFER.equals(tval))
return 0;
try
{
Int32Type.instance.validate(tval);
}
catch (MarshalException e)
{
throw new InvalidRequestException("Invalid TTL value: " + tval);
}
int ttl = Int32Type.instance.compose(tval);
if (ttl < 0)
throw new InvalidRequestException("A TTL must be greater or equal to 0, but was " + ttl);
if (ttl > MAX_TTL)
throw new InvalidRequestException(String.format("ttl is too large. requested (%d) maximum (%d)", ttl, MAX_TTL));
if (metadata.params.defaultTimeToLive != LivenessInfo.NO_TTL && ttl == LivenessInfo.NO_TTL)
return LivenessInfo.NO_TTL;
ExpirationDateOverflowHandling.maybeApplyExpirationDateOverflowPolicy(metadata, ttl, false);
return ttl;
}
public void collectMarkerSpecification(VariableSpecifications boundNames)
{
if (timestamp != null)
timestamp.collectMarkerSpecification(boundNames, this);
if (timeToLive != null)
timeToLive.collectMarkerSpecification(boundNames, this);View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Clamp or validate the TTL client-side: if (ttl < 0) ttl = 0; or reject before executing
- Use 0 to mean 'no TTL' instead of negative sentinels
- Fix the calculation producing the negative duration (e.g. expiry - now when expiry already passed)
- Re-run the statement with a non-negative TTL
Example fix
// before int ttl = (int) Duration.between(now, expiry).getSeconds(); // may be negative // after int ttl = Math.max(0, (int) Duration.between(now, expiry).getSeconds());
Defensive patterns
Strategy: validation
Validate before calling
int safeTtl(int requested) {
if (requested < 0)
throw new IllegalArgumentException("TTL must be >= 0; use 0 for no expiry");
return requested;
} Type guard
boolean isValidTtl(Integer ttl) { return ttl != null && ttl >= 0; } Try / catch
try {
session.execute(bs);
} catch (InvalidQueryException e) {
if (e.getMessage().startsWith("A TTL must be greater or equal to 0"))
log.error("Negative TTL bound; use 0 for no-expiry, not negative sentinels");
throw e;
} Prevention
- Use 0 as the 'no TTL' sentinel, never negative values
- Clamp computed durations with Math.max(0, ttl)
- Validate user-supplied TTLs at the API boundary
- Beware of arithmetic underflow when deriving TTLs from dates
When it happens
Trigger: 'USING TTL ?' bound with a negative int value (e.g. -1) coming from client code, computed values, or overflow of arithmetic on the client side.
Common situations: Passing -1 as a sentinel for 'no TTL' (the correct sentinel is 0); subtraction/date math producing negative durations; configuration default TTL values entered as negative numbers.
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
- ttl is too large. requested (%d) maximum (%d)
- Invalid null value of timestamp
- Invalid TTL value: <tval>
- Global TTL on the BATCH statement is not supported.
- Counter and non-counter mutations cannot exist in the same b
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/53b7c49d31cb01cf.
Report an issue: GitHub.