apache/cassandra · error · InvalidRequestException

Batch with conditions cannot span multiple partitions

Error message

Batch with conditions cannot span multiple partitions

What it means

Conditional (LWT) batches in newer Cassandra versions must operate on a single partition: all statements' partition keys must be identical, since Paxos coordination is per-partition. In makeCasRequest, when collected partition keys differ, an InvalidRequestException is thrown naming the multi-partition violation.

Source

Thrown at src/java/org/apache/cassandra/cql3/statements/BatchStatement.java:631

        CQL3CasRequest casRequest = null;
        Set<ColumnMetadata> columnsWithConditions = new LinkedHashSet<>();

        for (int i = 0; i < statements.size(); i++)
        {
            ModificationStatement statement = statements.get(i);
            QueryOptions statementOptions = options.forStatement(i);
            long timestamp = attrs.getTimestamp(batchTimestamp, statementOptions);
            List<ByteBuffer> pks = statement.buildPartitionKeyNames(statementOptions, state.getClientState());
            if (statement.getRestrictions().keyIsInRelation())
                throw new IllegalArgumentException("Batch with conditions cannot span multiple partitions (you cannot use IN on the partition key)");
            if (key == null)
            {
                key = statement.metadata().partitioner.decorateKey(pks.get(0));
                casRequest = new CQL3CasRequest(statement.metadata(), key, conditionColumns, updatesRegularRows, updatesStaticRow, requestTime);
            }
            else if (!key.getKey().equals(pks.get(0)))
            {
                throw new InvalidRequestException("Batch with conditions cannot span multiple partitions");
            }

            checkFalse(statement.getRestrictions().clusteringKeyRestrictionsHasIN(),
                       "IN on the clustering key columns is not supported with conditional %s",
                       statement.type.isUpdate()? "updates" : "deletions");

            if (statement.hasSlices())
            {
                // All of the conditions require meaningful Clustering, not Slices
                assert !statement.hasConditions();

                Slices slices = statement.createSlices(statementOptions);
                // If all the ranges were invalid we do not need to do anything.
                if (slices.isEmpty())
                    continue;

                for (Slice slice : slices)
                {

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Restructure the conditional batch so every statement targets the same partition key
  2. Move cross-partition conditional logic into the application (read-check-write with retries) or use a single-row/denormalized design
  3. If multi-partition atomicity is essential, consider a different data model or an external coordination mechanism

Example fix

// before
BEGIN BATCH UPDATE t SET v=1 WHERE pk=1 IF v=0;
             UPDATE t SET v=2 WHERE pk=2 IF v=0; APPLY BATCH;
// after
BEGIN BATCH UPDATE t SET v=1 WHERE pk=1 IF v=0;
             UPDATE t SET v=2 WHERE pk=1 IF v=0; APPLY BATCH;
Defensive patterns

Strategy: validation

Validate before calling

if (batchHasConditions && stmts.stream().map(s -> s.partitionKey()).distinct().count() > 1)
    throw new IllegalArgumentException("LWT batch must target a single partition");

Try / catch

catch (InvalidRequestException e) { if (e.getMessage().contains("cannot span multiple partitions")) { splitPerPartitionAndRetry(); } else throw e; }

Prevention

When it happens

Trigger: A conditional batch (BEGIN BATCH ... IF) whose statements touch rows in two or more partitions — e.g. 'UPDATE t SET v=1 WHERE pk=1 IF x=1; UPDATE t SET v=2 WHERE pk=2 IF y=1'.

Common situations: Applications treating LWT batches as multi-row transactions across partitions; relational-style transaction porting; clustering-key writes accidentally using different partition key values.

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


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