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

  1. Read the `Expected ... found ...` span and insert/replace the required token
  2. Compare with a working PRQL example in the book
  3. 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

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)