apache/cassandra · error · InvalidRequestException
PRIMARY KEY column "%s" cannot be restricted (preceding colu
Error message
PRIMARY KEY column "%s" cannot be restricted (preceding column "%s" is restricted by a non-EQ relation)
What it means
Cassandra rejects CQL WHERE clauses where a clustering (PRIMARY KEY) column is restricted while an earlier clustering column is only restricted by a non-EQ relation (e.g. a slice like > or <). Since clustering columns are sorted, a restriction on a later column only makes sense when all preceding columns are fixed by EQ relations; otherwise the result set is ill-defined without filtering.
Source
Thrown at src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java:98
{
SingleRestriction newRestriction = (SingleRestriction) restriction;
RestrictionSet newRestrictionSet = restrictions.addRestriction(newRestriction);
if (!isEmpty() && !allowFiltering && (indexRegistry == null || !newRestriction.hasSupportingIndex(indexRegistry, indexHints)))
{
SingleRestriction lastRestriction = restrictions.lastRestriction();
assert lastRestriction != null;
ColumnMetadata lastRestrictionStart = lastRestriction.firstColumn();
ColumnMetadata newRestrictionStart = restriction.firstColumn();
checkFalse(lastRestriction.isSlice() && newRestrictionStart.position() > lastRestrictionStart.position(),
"Clustering column \"%s\" cannot be restricted (preceding column \"%s\" is restricted by a non-EQ relation)",
newRestrictionStart.name,
lastRestrictionStart.name);
if (newRestrictionStart.position() < lastRestrictionStart.position() && newRestriction.isSlice())
throw invalidRequest("PRIMARY KEY column \"%s\" cannot be restricted (preceding column \"%s\" is restricted by a non-EQ relation)",
restrictions.nextColumn(newRestrictionStart).name,
newRestrictionStart.name);
}
return new ClusteringColumnRestrictions(this.comparator, newRestrictionSet, allowFiltering, partitioner);
}
public NavigableSet<Clustering<?>> valuesAsClustering(QueryOptions options, ClientState state) throws InvalidRequestException
{
// fast path, a typical case when a single full restriction is used
// for example, when we specify a single clustering key (a single row) to insert/update
if (restrictions.size() == 1 && !restrictions.hasIN())
{
SingleRestriction r = restrictions.lastRestriction();
List<ClusteringElements> values = r.values(options);
return MultiCBuilder.build(comparator, values);
}
MultiCBuilder builder = new MultiCBuilder(comparator);View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Restrict all clustering columns preceding the sliced column with EQ relations, or drop the restriction on later clustering columns
- Use ALLOW FILTERING to explicitly accept a non-efficient query, though this does not bypass the primary-key ordering rule in all cases
- Model the data differently: denormalize into a table whose clustering order matches your access pattern
Example fix
// before SELECT * FROM sensor_data WHERE device_id = 1 AND reading_time > '2026-01-01' AND metric = 'temp'; // after SELECT * FROM sensor_data WHERE device_id = 1 AND metric = 'temp' AND reading_time > '2026-01-01';
Defensive patterns
Strategy: validation
Validate before calling
// before issuing: ensure EQ restrictions precede any slice on clustering columns
ClusterMetaData cmd = table.clusteringColumns();
boolean seenSlice = false;
for (ColumnMetadata c : cmd) {
Restriction r = where.restrictionFor(c);
if (seenSlice && r != null) throw new IllegalArgumentException("restricting '" + c.name + "' after a non-EQ restriction");
if (r != null && r.isSlice()) seenSlice = true;
} Prevention
- Design clustering order to match your query patterns up front
- Verify with a dry parse (prepare statement) in tests for all query templates
- Avoid mixing slice and later-column EQ restrictions without ALLOW FILTERing fallback logic
When it happens
Trigger: A mergeWith of clustering restrictions occurs when parsing a query like: SELECT ... WHERE clustering1 > 5 AND clustering2 = 3. The slice restriction on clustering1 at an earlier position, plus an EQ on a later column clustering2, triggers this in ClusteringColumnRestrictions.mergeWith.
Common situations: Developers write range queries on multi-column clustering keys assuming per-column filtering works like SQL. It appears after schema changes that add clustering columns, or when porting SQL-style queries to CQL.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- %s constraint can not be specified on a %s key column '%s'
- Cannot use selection function %s on PRIMARY KEY part %s
- Column "%s" cannot be restricted by two inequalities not sta
- More than one restriction was found for the start bound on %
- More than one restriction was found for the end bound on %s
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/2096873192728469.
Report an issue: GitHub.