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

  1. Move the non-counter statement(s) into a separate batch
  2. 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

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


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/2311bf31928bd37f. Report an issue: GitHub.