apache/cassandra · error · InvalidRequestException
Cannot include non-counter statement in a counter batch
Error message
Cannot include non-counter statement in a counter batch
What it means
InvalidRequestException from BatchStatement.validate: the batch was declared/typed as a counter batch (COUNTER BATCH) but contains at least one statement targeting a non-counter table. validate() accumulates hasCounters/hasNonCounters flags per statement; the isCounter() && hasNonCounters combination triggers this guard.
Source
Thrown at src/java/org/apache/cassandra/cql3/statements/BatchStatement.java:294
if (timestampSet && statement.isTimestampSet())
throw new InvalidRequestException("Timestamp must be set either on BATCH or individual statements: " + statement.source);
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)
{View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Move the non-counter statement(s) into a separate batch
- Verify table schemas — ensure all batched statements target counter tables when writing counters
Example fix
// before BEGIN BATCH UPDATE ctr SET c=c+1 WHERE k='a'; INSERT INTO t (k,v) VALUES (1,'x'); APPLY BATCH; // after BEGIN BATCH UPDATE ctr SET c=c+1 WHERE k='a'; APPLY BATCH; INSERT INTO t (k,v) VALUES (1,'x');
Defensive patterns
Strategy: validation
Validate before calling
boolean counterBatch = statements.stream().allMatch(ModificationStatement::isCounter);
boolean mixed = statements.stream().anyMatch(s -> s.isCounter() != counterBatch);
if (mixed) throw new IllegalArgumentException("counter and non-counter statements must not share a batch"); Try / catch
try { session.execute(batch); }
catch (InvalidQueryException e) {
if (e.getMessage().contains("non-counter statement in a counter batch")) { /* partition statements by table type */ }
} Prevention
- Group batched statements by table type before building batches
- Verify target tables are COUNTER type when generating counter batches
When it happens
Trigger: BEGIN BATCH ... where the batch was detected as a counter batch (all/marked counter) yet includes at least one statement against a non-counter table, via execute or prepare.
Common situations: Accidentally adding a regular-table write to a batch of counter increments; table schema changed from counter to regular (or vice versa) so statements no longer agree.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Cannot provide custom timestamp for counter BATCH
- Cannot provide custom timestamp for a BATCH containing count
- Global TTL on the BATCH statement is not supported.
- Cannot provide custom timestamp for conditional BATCH
- Timestamp must be set either on BATCH or individual statemen
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/2311bf31928bd37f.
Report an issue: GitHub.