apache/cassandra · error · InvalidRequestException
Global TTL on the BATCH statement is not supported.
Error message
Global TTL on the BATCH statement is not supported.
What it means
BATCH statements do not accept a batch-level USING TTL. BatchStatement.validate() rejects batches whose StatementAttributes have timeToLive set, because per-row TTLs must come from the individual INSERT/UPDATE statements.
Source
Thrown at src/java/org/apache/cassandra/cql3/statements/BatchStatement.java:256
@Override
public boolean eligibleAsPreparedStatement()
{
return true;
}
@Override
public void authorize(ClientState state) throws InvalidRequestException, UnauthorizedException
{
ModificationStatement.TableAuthorizationState authorizationState = new ModificationStatement.TableAuthorizationState();
for (ModificationStatement statement : statements)
statement.authorize(state, authorizationState);
}
// Validates a prepared batch statement without validating its nested statements.
public void validate() throws InvalidRequestException
{
if (attrs.isTimeToLiveSet())
throw new InvalidRequestException("Global TTL on the BATCH statement is not supported.");
boolean timestampSet = attrs.isTimestampSet();
if (timestampSet)
{
if (hasConditions)
throw new InvalidRequestException("Cannot provide custom timestamp for conditional BATCH");
if (isCounter())
throw new InvalidRequestException("Cannot provide custom timestamp for counter BATCH");
}
boolean hasCounters = false;
boolean hasNonCounters = false;
boolean hasVirtualTables = false;
boolean hasRegularTables = false;
for (ModificationStatement statement : statements)View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Remove USING TTL from the BATCH header and add USING TTL to each individual statement inside the batch
- If a uniform TTL is needed, generate the statements programmatically with the TTL applied per statement
Example fix
// before BEGIN BATCH INSERT INTO t ... USING TTL 86400 APPLY BATCH; // after BEGIN BATCH INSERT INTO t ... USING TTL 86400; APPLY BATCH;
Defensive patterns
Strategy: validation
Validate before calling
if (batchCql.matches("(?is)BEGIN\\s+(UNLOGGED|COUNTER\\s+)?BATCH.*USING\\s+TTL\\b"))
throw new IllegalArgumentException("USING TTL is not allowed on BATCH; apply per statement"); Try / catch
try { session.execute(batch); }
catch (InvalidQueryException e) {
if (e.getMessage().contains("Global TTL on the BATCH")) { /* rewrite statements with per-row TTL */ }
} Prevention
- Always put USING TTL on individual INSERT/UPDATE statements, never on the BATCH header
- Encode this rule in query-builder tests
When it happens
Trigger: Executing BEGIN BATCH ... USING TTL 86400 ... APPLY BATCH, or preparing such a statement (validate is called from prepare and processBatch).
Common situations: Copy-pasting a single-statement INSERT ... USING TTL into a batch wrapper; ORM/query builders hoisting a global TTL onto the batch.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Invalid TTL value: <tval>
- A TTL must be greater or equal to 0, but was <ttl>
- ttl is too large. requested (%d) maximum (%d)
- Cannot provide custom timestamp for conditional BATCH
- Cannot provide custom timestamp for counter BATCH
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/30bc5cfd696899d5.
Report an issue: GitHub.