PRQL/prql · error · Error
Expected , found
Error message
Expected {expected_str}, found {found_str} What it means
The parser converts a `RichReason::Expected` into `Reason::Expected` when a construct was expected but something else was found. The message reads `Expected X, found Y` (with `one of a, b or c` when multiple tokens/keywords were acceptable).
Solutions
- Read the `Expected ... found ...` span and insert/replace the required token
- Compare with a working PRQL example in the book
- Run `prqlc fmt` on smaller chunks to isolate the malformed segment
Example fix
// before from employees filter age > 30 // after from employees | filter age > 30
Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
try { compile(prql) } catch (e) { if (e.reason === 'Expected') { hint(e.expected, e.span) } else { throw e } } Prevention
- Insert pipe separators between transforms
- Cross-check keyword names against the PRQL book
- Validate incrementally: compile after each transform is added
When it happens
Trigger: Structurally invalid query where a token/keyword was required: e.g. missing `|` between transforms, wrong keyword in a transform position, malformed function-call syntax passed to `prqlc compile`.
Common situations: Mixing SQL keyword order into PRQL, forgetting pipe characters, writing `sort`/`take` variants that don't exist, editing a valid query into an invalid one.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
AI-assisted analysis of PRQL/prql@e164e249b9 (2026-09-09).
Data as JSON: /api/errors/34c7ecabbdae87db.
Report an issue: GitHub.
Appendix: source
Thrown at prqlc/prqlc-parser/src/parser/perror.rs:67
};
if expected_strs.is_empty() || expected_strs.len() > 10 {
Error::new_simple(format!("unexpected {found_str}"))
} else {
let mut expected_strs = expected_strs;
expected_strs.sort();
let expected_str = match expected_strs.len() {
1 => expected_strs[0].clone(),
2 => expected_strs.join(" or "),
_ => {
let last = expected_strs.pop().unwrap();
format!("one of {} or {last}", expected_strs.join(", "))
}
};
match found {
Some(_) => Error::new(Reason::Expected {
who: None,
expected: expected_str,
found: found_str,
}),
None => Error::new(Reason::Simple(format!(
"Expected {expected_str}, but didn't find anything before the end."
))),
}
}
}
RichReason::Custom(msg) => Error::new_simple(msg.to_string()),
};
error.with_span(Some(span))
}
impl<'a> From<Rich<'a, crate::lexer::lr::Token, Span>> for Error {
fn from(rich: Rich<'a, crate::lexer::lr::Token, Span>) -> Error {View on GitHub (pinned to e164e249b9)