{"record":{"id":"a80eac0790a23929","repo":"apache/cassandra","slug":"range-deletions-must-specify-a-single-partition-ke","errorCode":null,"errorMessage":"Range deletions must specify a single partition key in the underlying table ","messagePattern":"Range deletions must specify a single partition key in the underlying table ","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/db/virtual/RemoteToLocalVirtualTable.java","lineNumber":535,"sourceCode":"\n        PartitionUpdate.Builder builder = null;\n        ArrayDeque<Promise<Void>> results = new ArrayDeque<>();\n\n        if (deletionInfo.hasRanges())\n        {\n            Iterator<RangeTombstone> iterator = deletionInfo.rangeIterator(false);\n            while (iterator.hasNext())\n            {\n                RangeTombstone rt = iterator.next();\n                ClusteringBound start = rt.deletedSlice().start();\n                ClusteringBound end = rt.deletedSlice().end();\n                if (start.size() < pkCount || end.size() < pkCount)\n                    throw new InvalidRequestException(\"Range deletions must specify a complete partition key in the underlying table \" + metadata);\n\n                for (int i = 0 ; i < pkCount ; ++i)\n                {\n                    if (0 != start.accessor().compare(start.get(i), end.get(i), end.accessor()))\n                        throw new InvalidRequestException(\"Range deletions must specify a single partition key in the underlying table \" + metadata);\n                }\n\n                DecoratedKey key = remoteClusteringToLocalPartitionKey(local, start, pkCount, pkBuffer);\n                builder = maybeRolloverAndWait(key, builder, results, endpoint);\n                if (start.size() == pkCount && end.size() == pkCount)\n                {\n                    builder.addPartitionDeletion(rt.deletionTime());\n                }\n                else\n                {\n                    start = ClusteringBound.create(start.kind(), Clustering.make(remoteClusteringToLocalClustering(start.clustering(), pkCount, ckBuffer)));\n                    end = ClusteringBound.create(end.kind(), Clustering.make(remoteClusteringToLocalClustering(end.clustering(), pkCount, ckBuffer)));\n                    builder.add(new RangeTombstone(Slice.make(start, end), rt.deletionTime()));\n                }\n            }\n        }\n\n        if (!update.staticRow().isEmpty())","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/db/virtual/RemoteToLocalVirtualTable.java#L517-L553","documentation":"A range tombstone whose start and end bounds are both full partition keys but not identical covers more than one underlying partition; the virtual table only supports deletions mapping to a single partition key per tombstone, so it throws InvalidRequestException.","triggerScenarios":"Range tombstone where start.size() == end.size() == pkCount but some component i has start.get(i) != end.get(i), i.e. the deletion spans multiple underlying partition keys (e.g. clustering BETWEEN a AND b with distinct mapped keys).","commonSituations":"DELETE with a BETWEEN/inequality range over clustering columns that map to different underlying partitions; batch statements mixing such deletes.","solutions":["Issue one exact-key DELETE per row/partition instead of a spanning range.","Restrict the range so start and end map to the same partition key (equal on all pk components).","Perform the deletion on the underlying table directly on each owning node.","If bulk removal is intended, use a partition-level or truncate operation (apply() supports partition deletion via truncate)."],"exampleFix":"// before\nDELETE FROM vt WHERE ck >= 1 AND ck <= 5;  // spans multiple partitions\n// after\nDELETE FROM vt WHERE ck = 1; DELETE FROM vt WHERE ck = 2; // ...one per key","handlingStrategy":"validation","validationCode":"// Range deletes must collapse to one partition key\nif (!startBound.equals(endBound))\n    throw new IllegalArgumentException(\"Range spans multiple partitions; use per-key deletes\");","typeGuard":null,"tryCatchPattern":"try { session.execute(delete); }\ncatch (InvalidRequestException e) {\n    if (e.getMessage().contains(\"single partition key\"))\n        // split range into individual key deletes\n}\n","preventionTips":["Never DELETE across a range that maps to multiple partitions","Enumerate and delete each key explicitly","Perform bulk deletes on the underlying table directly if needed","Test delete statements against multi-key ranges in staging"],"tags":["cassandra","virtual-table","range-tombstone","delete"],"backgroundTag":"unsupported-operation","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}