apache/cassandra · error · InvalidRequestException
Counter and non-counter mutations cannot exist in the same b
Error message
Counter and non-counter mutations cannot exist in the same batch
What it means
Cassandra batches must be homogeneous with respect to counter mutations: counters use a different replication/update path than regular writes, so a single BATCH cannot mix counter and non-counter statements. BatchStatement.validate() detects the combination of hasCounters && hasNonCounters and rejects the batch before execution with InvalidRequestException.
Source
Thrown at src/java/org/apache/cassandra/cql3/statements/BatchStatement.java:297
if (statement.isCounter())
hasCounters = true;
else
hasNonCounters = true;
if (statement.isVirtual())
hasVirtualTables = true;
else
hasRegularTables = true;
}
if (timestampSet && hasCounters)
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)View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Split into two separate BATCH statements: one for counter mutations, one for regular mutations
- Execute the counter mutation and the regular mutation as independent statements in the same logical transaction of the application
- Redesign the schema if counter and regular data must be written atomically (counters cannot participate in atomic batches anyway)
Example fix
// before BEGIN BATCH UPDATE page_counts SET views = views + 1 WHERE page = 'home'; INSERT INTO events (id, data) VALUES (1, 'x'); APPLY BATCH; // after UPDATE page_counts SET views = views + 1 WHERE page = 'home'; BEGIN BATCH INSERT INTO events (id, data) VALUES (1, 'x'); APPLY BATCH;
Defensive patterns
Strategy: validation
Validate before calling
boolean hasCounter = tables.stream().anyMatch(t -> isCounterTable(t));
boolean hasRegular = tables.stream().anyMatch(t -> !isCounterTable(t));
if (hasCounter && hasRegular) throw new IllegalArgumentException("Split batch: counters and non-counters cannot share a BATCH"); Try / catch
catch (InvalidRequestException e) { if (e.getMessage().contains("Counter and non-counter")) { splitAndRetrySeparately(); } else throw e; } Prevention
- Track which of your tables are counter tables and route their writes through separate batch builders
- Keep counter mutations in dedicated code paths, never in generic batch utilities
- Add unit tests that assert batch builders never mix table kinds
When it happens
Trigger: A single BATCH statement contains at least one INSERT/UPDATE/DELETE on a table with a counter column and at least one mutation on a regular (non-counter) table, e.g. 'BEGIN BATCH UPDATE counters SET c=c+1 ...; INSERT INTO regular ...; APPLY BATCH'.
Common situations: Applications that batch related writes across tables where one table was later migrated to counters; ORM/query builders that assemble batches dynamically; users unaware counters can't be batched with normal writes.
Related errors
- Cannot include a counter statement in a logged batch
- Invalid empty consistency level
- There were %d markers(?) in CQL but %d bound variables
- Invalid statement in batch: only UPDATE, INSERT and DELETE s
- LIST PERMISSIONS operation is not supported by AllowAllAutho
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/94cf3216003dd647.
Report an issue: GitHub.