Prometheus Error Guide: 'parse error: unexpected' PromQL Syntax Errors
Fix PromQL 'parse error: unexpected character/identifier' and 'no arguments for aggregate expression' errors: unbalanced brackets, range selectors, and aggregation syntax.
- #prometheus-monitoring
- #troubleshooting
- #errors
- #promql
Exact Error Message
PromQL parse errors are returned by the query engine before any data is touched. They appear in the Graph/Explore UI as a red banner, and in the HTTP API as a 400 with status: "error" and errorType: "bad_data". The exact text varies by mistake:
1:15: parse error: unexpected character: '}'
parse error at char 24: unexpected "]" in aggregation, expected ")"
parse error: no arguments for aggregate expression provided
1:8: parse error: unexpected identifier "by" in label matching, expected one of "," or "}"
The leading line:column (e.g. 1:15) is a one-based pointer into your expression. The number after char in parse error at char N is a zero-based byte offset.
What the Error Means
PromQL has a formal grammar, and the parser rejects any expression that does not match it. Unlike a runtime error (division by an empty vector, a type mismatch), a parse error means the engine never even built a query plan — the string you sent is not valid PromQL.
Every parse error pins the exact offset where the grammar broke. The token the parser found is reported as unexpected ..., and frequently it also tells you what it expected (expected ")"). Read both halves: the fix is almost always at or just before the reported column. Because this is a pure syntax check, it is independent of whether the metric exists, whether any target is up, or what time range you pick.
Common Causes
1. Unbalanced brackets, parentheses, or braces
The single most common cause. A missing ), an extra }, or a [ without a matching ]:
sum(rate(http_requests_total[5m]) # missing closing paren
rate(http_requests_total[5m]}) # stray brace
2. Label matcher quoting and operators
Matcher values must be double-quoted strings, and only =, !=, =~, !~ are valid:
http_requests_total{job=api} # value not quoted
http_requests_total{job=="api"} # == is not a matcher operator
http_requests_total{job~="api.*"} # regex op is =~ not ~=
3. Range selector [5m] used where an instant vector is required
A range vector (metric[5m]) cannot be graphed or returned directly by an instant query, nor passed to functions that want an instant vector:
http_requests_total[5m] # range vector — instant query rejects it
sum(http_requests_total[5m]) # sum() wants an instant vector
4. Aggregation by / without clause placement
by (...) and without (...) attach to the aggregation operator, and the label list lives in parentheses:
sum http_requests_total by (job) # missing parens around the arg
sum by job (http_requests_total) # label list needs parentheses
sum(http_requests_total) by (job) # by must hug the operator, before or after the arg
Both sum by (job) (expr) and sum(expr) by (job) are valid; mixing them up trips the parser.
5. Wrong function arity
Calling a function with too few or too many arguments. no arguments for aggregate expression provided is the classic — an aggregator with an empty argument list:
sum() # aggregate needs an instant vector
histogram_quantile(req_bucket) # needs (φ, vector): two args
rate(http_requests_total) # rate() needs a RANGE vector: rate(metric[5m])
6. Reserved words and the empty by () clause
Aggregation keywords (by, without, on, ignoring, group_left, bool, offset) are reserved. Using one as a bare metric or label name, or writing by () with an empty grouping where the parser expects labels, breaks parsing.
How to Reproduce the Error
Send a deliberately malformed expression to the instant query endpoint:
curl -s 'http://localhost:9090/api/v1/query' \
--data-urlencode 'query=sum(rate(http_requests_total[5m])' | jq .
{
"status": "error",
"errorType": "bad_data",
"error": "1:5: parse error: unexpected end of input, expected \")\""
}
The same string in the Graph UI underlines the offending position. promtool reproduces it offline:
promtool query instant http://localhost:9090 'sum http_requests_total by (job)'
query error: ... parse error: unexpected identifier "http_requests_total"
Diagnostic Commands
Validate the raw expression against the parser, no data required:
curl -s 'http://localhost:9090/api/v1/query' \
--data-urlencode 'query=http_requests_total{job=api}' \
| jq -r '.error'
1:24: parse error: unexpected identifier "api" in label matching, expected string
Use promtool query instant to get the same parser feedback from the CLI, which is convenient in CI:
promtool query instant http://localhost:9090 'rate(http_requests_total)'
Bisect a long expression by stripping it back to the innermost selector and rebuilding outward — paste each layer into Explore until the red banner appears, which isolates the broken layer:
http_requests_total{job="api"}
rate(http_requests_total{job="api"}[5m])
sum by (instance) (rate(http_requests_total{job="api"}[5m]))
Step-by-Step Resolution
-
Read the column pointer.
1:15means character 15. Put your cursor there; the bad token is at or just before it. -
Balance every bracket. Count
(/),[/],{/}. Most editors will match-highlight; an unmatched opener is your culprit.sum(rate(http_requests_total[5m])) -
Fix matcher quoting and operators. Quote values, use
=~for regex:http_requests_total{job="api", status=~"5.."} -
Give each function the right vector type.
rate(),increase(),irate(),delta(),deriv()take a range vector;sum(),abs(),histogram_quantile()take an instant vector:sum(rate(http_requests_total[5m])) # rate over range, then sum the instant result histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m]))) -
Place
by/withoutcorrectly and never leave the label list out of parentheses:sum by (job) (rate(http_requests_total[5m])) -
Re-run the corrected query in Explore or with
promtool query instant. A200with aresultTypeofvector/matrix/scalarconfirms the syntax is valid (an empty result is a different problem — the syntax is fine).
Prevention and Best Practices
- Author and test queries in the Explore/Graph UI or with
promtool query instantbefore pasting them into alert rules, recording rules, or dashboards. - Lint rule files in CI with
promtool check rules rules.yml, which catches parse errors at PR time rather than at reload. - Keep nesting shallow and build outward: get the bare selector working, add
rate(), then aggregate. Validate each layer. - Standardize on one
by/withoutstyle across your team so reviewers spot misplaced clauses instantly. - The free incident assistant can rewrite a broken PromQL expression into a valid one and explain the fix; browse more under Prometheus and monitoring.
Related Errors
- Prometheus Error Guide: ‘binary expression must contain only scalar and instant vector types’ — a type error that surfaces after the expression parses cleanly.
- Prometheus Error Guide: ‘Empty query result’ / No data — valid syntax, but the query returns nothing.
- Prometheus Error Guide: ‘rate should only be used with counters’ —
rate()parses fine but is applied to the wrong metric type.
Frequently Asked Questions
What does the number in parse error at char 24 mean? It is a zero-based byte offset into your query string pointing at the unexpected token. The 1:15 form is the same location expressed as one-based line:column. Jump there first.
Why do I get no arguments for aggregate expression provided? You called an aggregator (sum, avg, max, count, etc.) with an empty argument list — sum() or sum by (job) (). Aggregators require an instant-vector argument: sum(rate(http_requests_total[5m])).
My query works in Explore but fails in an alert rule. Why? YAML quoting. A PromQL expression embedded in expr: may need block scalar style (expr: |) or careful escaping so that braces and quotes survive YAML parsing. Run promtool check rules to see the post-YAML expression the parser receives.
Is rate(http_requests_total) a parse error or a type error? It is a parse-time complaint about arity/type: rate() requires a range vector, so you must write rate(http_requests_total[5m]). Without the [5m] the engine has no range to compute a rate over.
Can I validate PromQL without a running Prometheus? Yes. promtool query instant needs a server, but promtool check rules file.yml parses every expression in a rule file offline and reports parse errors with their positions.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.