apache/cassandra · error · InvalidRequestException
Conditional BATCH statements cannot include mutations for vi
Error message
Conditional BATCH statements cannot include mutations for virtual tables
What it means
InvalidRequestException from BatchStatement.validate: the batch carries conditions (IF ... — a conditional/LWT batch) and includes mutations against virtual tables. Virtual tables cannot participate in CAS/conditional logic, so their presence alongside conditions is rejected during batch validation (companion guards reject counters in logged batches and mixed counter/non-counter batches).
Source
Thrown at src/java/org/apache/cassandra/cql3/statements/BatchStatement.java:309
throw new InvalidRequestException("Cannot provide custom timestamp for a BATCH containing counters");
if (isCounter() && hasNonCounters)
throw new InvalidRequestException("Cannot include non-counter statement in a counter batch");
if (hasCounters && hasNonCounters)
throw new InvalidRequestException("Counter and non-counter mutations cannot exist in the same batch");
if (isLogged() && hasCounters)
throw new InvalidRequestException("Cannot include a counter statement in a logged batch");
if (isLogged() && hasVirtualTables)
throw new InvalidRequestException("Cannot include a virtual table statement in a logged batch");
if (hasVirtualTables && hasRegularTables)
throw new InvalidRequestException("Mutations for virtual and regular tables cannot exist in the same batch");
if (hasConditions && hasVirtualTables)
throw new InvalidRequestException("Conditional BATCH statements cannot include mutations for virtual tables");
if (hasConditions)
{
String ksName = null;
String cfName = null;
for (ModificationStatement stmt : statements)
{
if (ksName != null && (!stmt.keyspace().equals(ksName) || !stmt.table().equals(cfName)))
throw new InvalidRequestException("Batch with conditions cannot span multiple tables: " + stmt.source);
ksName = stmt.keyspace();
cfName = stmt.table();
}
}
}
private boolean isCounter()
{
return type == Type.COUNTER;View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Remove virtual table statements from conditional batches — LWT applies only to regular tables
- Move the conditional logic to regular table mutations only
- If conditional behavior on virtual data is needed, implement it application-side
Example fix
// before
BEGIN BATCH UPDATE ks.t SET v = 2 WHERE k = 1 IF v = 1;
UPDATE system_views.x SET a = 1 WHERE b = 2; APPLY BATCH;
// after
BEGIN BATCH UPDATE ks.t SET v = 2 WHERE k = 1 IF v = 1; APPLY BATCH;
Defensive patterns
Strategy: validation
Validate before calling
if (batchHasConditions && stmts.stream().anyMatch(s -> s.getTable().isVirtual()))
throw new IllegalArgumentException("LWT batches cannot include virtual tables"); Try / catch
catch (InvalidRequestException e) { if (e.getMessage().contains("Conditional BATCH") && e.getMessage().contains("virtual")) { dropVirtualStatementsAndRetry(); } else throw e; } Prevention
- Only attach IF conditions to statements on regular tables
- Audit generated LWT batches for virtual-table targets
When it happens
Trigger: 'BEGIN BATCH ... UPDATE system_views.v ... IF EXISTS; APPLY BATCH' — applying LWT conditions to virtual table mutations.
Common situations: Developers assuming virtual tables support compare-and-set like regular tables; generated conditional batches including virtual tables.
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
- Cannot provide custom timestamp for conditional BATCH
- Cannot include a virtual table statement in a logged batch
- Mutations for virtual and regular tables cannot exist in the
- Batch with conditions cannot span multiple tables: %s
- Invalid empty serial consistency level
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/9ba2c04b44040105.
Report an issue: GitHub.