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

  1. Split into two separate BATCH statements: one for counter mutations, one for regular mutations
  2. Execute the counter mutation and the regular mutation as independent statements in the same logical transaction of the application
  3. 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

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


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