{"record":{"id":"b18584e54b6291fc","repo":"apache/cassandra","slug":"batch-with-conditions-cannot-span-multiple-partiti","errorCode":null,"errorMessage":"Batch with conditions cannot span multiple partitions","messagePattern":"Batch with conditions cannot span multiple partitions","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/BatchStatement.java","lineNumber":631,"sourceCode":"        CQL3CasRequest casRequest = null;\n        Set<ColumnMetadata> columnsWithConditions = new LinkedHashSet<>();\n\n        for (int i = 0; i < statements.size(); i++)\n        {\n            ModificationStatement statement = statements.get(i);\n            QueryOptions statementOptions = options.forStatement(i);\n            long timestamp = attrs.getTimestamp(batchTimestamp, statementOptions);\n            List<ByteBuffer> pks = statement.buildPartitionKeyNames(statementOptions, state.getClientState());\n            if (statement.getRestrictions().keyIsInRelation())\n                throw new IllegalArgumentException(\"Batch with conditions cannot span multiple partitions (you cannot use IN on the partition key)\");\n            if (key == null)\n            {\n                key = statement.metadata().partitioner.decorateKey(pks.get(0));\n                casRequest = new CQL3CasRequest(statement.metadata(), key, conditionColumns, updatesRegularRows, updatesStaticRow, requestTime);\n            }\n            else if (!key.getKey().equals(pks.get(0)))\n            {\n                throw new InvalidRequestException(\"Batch with conditions cannot span multiple partitions\");\n            }\n\n            checkFalse(statement.getRestrictions().clusteringKeyRestrictionsHasIN(),\n                       \"IN on the clustering key columns is not supported with conditional %s\",\n                       statement.type.isUpdate()? \"updates\" : \"deletions\");\n\n            if (statement.hasSlices())\n            {\n                // All of the conditions require meaningful Clustering, not Slices\n                assert !statement.hasConditions();\n\n                Slices slices = statement.createSlices(statementOptions);\n                // If all the ranges were invalid we do not need to do anything.\n                if (slices.isEmpty())\n                    continue;\n\n                for (Slice slice : slices)\n                {","sourceCodeStart":613,"sourceCodeEnd":649,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java#L613-L649","documentation":"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.","triggerScenarios":"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'.","commonSituations":"Applications treating LWT batches as multi-row transactions across partitions; relational-style transaction porting; clustering-key writes accidentally using different partition key values.","solutions":["Restructure the conditional batch so every statement targets the same partition key","Move cross-partition conditional logic into the application (read-check-write with retries) or use a single-row/denormalized design","If multi-partition atomicity is essential, consider a different data model or an external coordination mechanism"],"exampleFix":"// before\nBEGIN BATCH UPDATE t SET v=1 WHERE pk=1 IF v=0;\n             UPDATE t SET v=2 WHERE pk=2 IF v=0; APPLY BATCH;\n// after\nBEGIN BATCH UPDATE t SET v=1 WHERE pk=1 IF v=0;\n             UPDATE t SET v=2 WHERE pk=1 IF v=0; APPLY BATCH;\n","handlingStrategy":"validation","validationCode":"if (batchHasConditions && stmts.stream().map(s -> s.partitionKey()).distinct().count() > 1)\n    throw new IllegalArgumentException(\"LWT batch must target a single partition\");","typeGuard":null,"tryCatchPattern":"catch (InvalidRequestException e) { if (e.getMessage().contains(\"cannot span multiple partitions\")) { splitPerPartitionAndRetry(); } else throw e; }","preventionTips":["Group conditional statements by partition key before batching","Model data so conditionally-updated rows share a partition when atomicity is needed"],"tags":["cql","batch","lwt","multiple-partitions"],"backgroundTag":"unsupported-operation","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}