PRQL/prql · error · Error
Expected , but didn't find anything before the end.
Error message
Expected {expected_str}, but didn't find anything before the end. What it means
When the parser expected a construct but reached end-of-input, `rich_error_to_error` builds a `Reason::Simple` with this message instead of `Expected X, found Y`. It means the query ended abruptly in the middle of a construct.
Solutions
- Complete the unfinished expression or close the open bracket at the reported location
- Check the file/stdin wasn't truncated (compare against source)
- Avoid a trailing `|` with nothing after it
Example fix
// before from employees | filter // 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 (String(e).includes("didn't find anything before the end")) { checkUnbalancedBrackets(prql) } else { throw e } } Prevention
- Balance-check parentheses/brackets before compiling
- Verify piped files/heredocs are not truncated
- Avoid trailing pipes with no following transform
When it happens
Trigger: Truncated input: unclosed parenthesis/bracket, dangling pipe at end of file, incomplete function definition, or empty argument at EOF passed to the parser.
Common situations: File cut off during save; heredoc/stdin piping lost trailing lines; half-typed query in watch mode; copy-paste that dropped the last line.
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/ef5f7b1429070a7d.
Report an issue: GitHub.
Appendix: source
Thrown at prqlc/prqlc-parser/src/parser/perror.rs:72
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 {
rich_error_to_error(
*rich.span(),
rich.reason(),
|token| format!("{}", token.kind),
|token| matches!(token.kind, TokenKind::NewLine | TokenKind::Start),View on GitHub (pinned to e164e249b9)