The "DB::Exception: Index of positional argument is out of range" error in ClickHouse occurs when a query uses positional arguments (numeric references) in GROUP BY, ORDER BY, or LIMIT BY clauses that exceed the number of columns in the SELECT list. The error code is INDEX_OF_POSITIONAL_ARGUMENT_IS_OUT_OF_RANGE (353). For example, ORDER BY 5 when the SELECT list has only 3 columns.
Impact
The query fails immediately during parsing/analysis and returns no results. This is a query syntax issue that does not affect any stored data or server state. The query must be corrected before it can execute.
Common Causes
- Positional index exceeds column count -- Using
ORDER BY 4when the SELECT has only 3 columns. - Column list was modified but ORDER BY was not updated -- Columns were added or removed from the SELECT list, but the positional references in ORDER BY or GROUP BY were not adjusted.
- Copy-paste errors -- A query was copied from another context with a different number of selected columns.
- Dynamic query generation bugs -- Application code that builds queries dynamically may compute incorrect positional indices.
- Zero or negative index -- Using
ORDER BY 0or a negative number, which is not valid. - Confusion with other SQL dialects -- Some databases use 0-based indexing, but ClickHouse uses 1-based indexing for positional arguments.
Troubleshooting and Resolution Steps
Count the columns in your SELECT list and compare with the positional argument:
-- This has 3 columns, so ORDER BY must be 1, 2, or 3 SELECT name, age, city FROM users ORDER BY 4; -- Error!Replace positional arguments with column names for clarity and safety:
-- Instead of: SELECT name, age, city FROM users ORDER BY 3; -- Use: SELECT name, age, city FROM users ORDER BY city;If using GROUP BY with positional arguments, verify the same way:
-- Ensure the number matches a valid column position SELECT status, count() FROM orders GROUP BY 1; -- OK if status is column 1Check for SELECT * expansion. If you use
SELECT *, the number of columns depends on the table schema. Using positional references withSELECT *is fragile:-- Avoid this pattern: SELECT * FROM users ORDER BY 5; -- May break if table schema changes -- Instead: SELECT * FROM users ORDER BY registration_date;For dynamically generated queries, add validation to ensure positional arguments are within range:
num_columns = len(select_columns) for pos in order_by_positions: assert 1 <= pos <= num_columns, f"ORDER BY position {pos} is out of range (1-{num_columns})"
Best Practices
- Prefer column names over positional arguments in ORDER BY and GROUP BY. Column names are self-documenting and resilient to SELECT list changes.
- If you must use positional arguments (common in ad-hoc queries), double-check the column count in the SELECT list.
- Avoid using positional arguments with
SELECT *, since the column count can change when the table schema is modified. - In application code that generates queries dynamically, always validate positional indices against the actual number of selected columns.
Frequently Asked Questions
Q: Does ClickHouse use 1-based or 0-based indexing for positional arguments?
A: ClickHouse uses 1-based indexing. ORDER BY 1 refers to the first column in the SELECT list.
Q: Can I use positional arguments in GROUP BY as well?
A: Yes. ClickHouse supports positional arguments in both ORDER BY and GROUP BY clauses, subject to the same rules. The position must be a valid index into the SELECT list.
Q: Is there a setting to disable positional arguments?
A: Yes. You can set enable_positional_arguments = 0 to disable this feature entirely, which will cause ClickHouse to treat numbers in ORDER BY as literal constants rather than column positions. This can help prevent accidental misuse.