{"record":{"id":"bdce5c00d0193d59","repo":"apache/cassandra","slug":"primary-key-column-s-cannot-be-restricted-as-pr","errorCode":null,"errorMessage":"PRIMARY KEY column \"%s\" cannot be restricted as preceding column \"%s\" is not restricted","messagePattern":"PRIMARY KEY column \"(.+?)\" cannot be restricted as preceding column \"(.+?)\" is not restricted","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java","lineNumber":705,"sourceCode":"            if (hasClusteringColumnsRestrictions() && clusteringColumnsRestrictions.needFiltering())\n            {\n                if (hasQueriableIndex || forView)\n                {\n                    usesSecondaryIndexing = true;\n                }\n                else if (!allowFiltering)\n                {\n                    List<ColumnMetadata> clusteringColumns = table.clusteringColumns();\n                    List<ColumnMetadata> restrictedColumns = new ArrayList<>(clusteringColumnsRestrictions.columns());\n\n                    for (int i = 0, m = restrictedColumns.size(); i < m; i++)\n                    {\n                        ColumnMetadata clusteringColumn = clusteringColumns.get(i);\n                        ColumnMetadata restrictedColumn = restrictedColumns.get(i);\n\n                        if (!clusteringColumn.equals(restrictedColumn))\n                        {\n                            throw invalidRequest(\"PRIMARY KEY column \\\"%s\\\" cannot be restricted as preceding column \\\"%s\\\" is not restricted\",\n                                                 restrictedColumn.name,\n                                                 clusteringColumn.name);\n                        }\n                    }\n                }\n            }\n\n        }\n    }\n\n    /**\n     * Returns the clustering columns that are not restricted.\n     * @return the clustering columns that are not restricted.\n     */\n    private Collection<ColumnIdentifier> getUnrestrictedClusteringColumns()\n    {\n        List<ColumnMetadata> missingClusteringColumns = new ArrayList<>(table.clusteringColumns());\n        missingClusteringColumns.removeAll(new LinkedList<>(clusteringColumnsRestrictions.columns()));","sourceCodeStart":687,"sourceCodeEnd":723,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java#L687-L723","documentation":"Clustering columns must be restricted in primary-key order without gaps: you may only restrict clustering column i if all columns before it are also restricted. When a restricted column skips over an unrestricted preceding clustering column, this error names both columns.","triggerScenarios":"SELECT * FROM t WHERE pk = 'a' AND c2 = 'x' while c1 (which precedes c2 in the PRIMARY KEY) is unrestricted; same gap pattern in DELETEs or IN relations.","commonSituations":"Querying the 'second' clustering key directly — a common modeling mistake; schema changes that reordered clustering columns; multi-column WHERE clauses generated from ORM filters.","solutions":["Add the missing preceding restriction: WHERE pk = ... AND c1 = ... AND c2 = ...","Use the token/partition design so the high-selectivity column comes first in the PRIMARY KEY definition","Restrict c1 with an IN list if you don't know its exact value: WHERE pk = ... AND c1 IN (...) AND c2 = ...","If filtering is genuinely needed, add ALLOW FILTERING (performs a scan within partitions)"],"exampleFix":"// before\nSELECT * FROM events WHERE device_id = 'd1' AND minute = 42;  -- hour (first clustering col) skipped\n// after\nSELECT * FROM events WHERE device_id = 'd1' AND hour = 1 AND minute = 42;","handlingStrategy":"validation","validationCode":"// Verify restricted clustering columns form a prefix of the clustering order:\nList<String> clusteringOrder = table.getClusteringColumns().stream().map(ColumnMetadata::getName).collect(Collectors.toList());\nList<String> restricted = whereColumns.stream().filter(clusteringOrder::contains).collect(Collectors.toList());\nif (!clusteringOrder.subList(0, restricted.size()).equals(restricted.stream().sorted(Comparator.comparingInt(clusteringOrder::indexOf)).collect(Collectors.toList()))) throw new IllegalArgumentException(\"Clustering columns must be restricted in order without gaps\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always restrict clustering columns left-to-right in PRIMARY KEY order","Order the PRIMARY KEY so the most-selective queried column comes first","Catch gap patterns in a query-construction layer built on table metadata"],"tags":["cql","cassandra","clustering-key"],"backgroundTag":"missing-required-argument","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}