apache/cassandra · error · InvalidRequestException
Range deletions must specify a single partition key in the…
Error message
Range deletions must specify a single partition key in the underlying table
What it means
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.
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).
Example fix
// before DELETE FROM vt WHERE ck >= 1 AND ck <= 5; // spans multiple partitions // after DELETE FROM vt WHERE ck = 1; DELETE FROM vt WHERE ck = 2; // ...one per key
Defensive patterns
Strategy: validation
Validate before calling
// Range deletes must collapse to one partition key
if (!startBound.equals(endBound))
throw new IllegalArgumentException("Range spans multiple partitions; use per-key deletes"); Try / catch
try { session.execute(delete); }
catch (InvalidRequestException e) {
if (e.getMessage().contains("single partition key"))
// split range into individual key deletes
}
Prevention
- 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
When it happens
Trigger: 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).
Common situations: DELETE with a BETWEEN/inequality range over clustering columns that map to different underlying partitions; batch statements mixing such deletes.
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
- Range deletions must specify a complete partition key in…
- Can only unset '" + name + "'
- Cannot filter this table by partial partition key
- Cannot set bucket_sub_size to zero.
- Cannot update '" + name + "'
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/a80eac0790a23929.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/db/virtual/RemoteToLocalVirtualTable.java:535
PartitionUpdate.Builder builder = null;
ArrayDeque<Promise<Void>> results = new ArrayDeque<>();
if (deletionInfo.hasRanges())
{
Iterator<RangeTombstone> iterator = deletionInfo.rangeIterator(false);
while (iterator.hasNext())
{
RangeTombstone rt = iterator.next();
ClusteringBound start = rt.deletedSlice().start();
ClusteringBound end = rt.deletedSlice().end();
if (start.size() < pkCount || end.size() < pkCount)
throw new InvalidRequestException("Range deletions must specify a complete partition key in the underlying table " + metadata);
for (int i = 0 ; i < pkCount ; ++i)
{
if (0 != start.accessor().compare(start.get(i), end.get(i), end.accessor()))
throw new InvalidRequestException("Range deletions must specify a single partition key in the underlying table " + metadata);
}
DecoratedKey key = remoteClusteringToLocalPartitionKey(local, start, pkCount, pkBuffer);
builder = maybeRolloverAndWait(key, builder, results, endpoint);
if (start.size() == pkCount && end.size() == pkCount)
{
builder.addPartitionDeletion(rt.deletionTime());
}
else
{
start = ClusteringBound.create(start.kind(), Clustering.make(remoteClusteringToLocalClustering(start.clustering(), pkCount, ckBuffer)));
end = ClusteringBound.create(end.kind(), Clustering.make(remoteClusteringToLocalClustering(end.clustering(), pkCount, ckBuffer)));
builder.add(new RangeTombstone(Slice.make(start, end), rt.deletionTime()));
}
}
}
if (!update.staticRow().isEmpty())View on GitHub (pinned to 88fd0f6a0e)