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

  1. Restrict all clustering columns preceding the sliced column with EQ relations, or drop the restriction on later clustering columns
  2. Use ALLOW FILTERING to explicitly accept a non-efficient query, though this does not bypass the primary-key ordering rule in all cases
  3. 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

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


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