BoundaryML/baml · error
block aware tail expression is not empty
Error message
block aware tail expression is not empty
What it means
This is a Rust panic from an `expect()` inside `parse_block_aware_tail_expression` in the BAML AST parser. The function asserts that a `block_aware_tail_expression` grammar rule always has at least one child pair (`into_inner().next()`); if the pest parse tree produced an empty node, the invariant is broken and the parser panics. It signals a mismatch between the grammar definition (.pest) and the parsing code, not user error in normal cases.
Source
Thrown at engine/baml-lib/ast/src/parser/parse_expr.rs:298
identifier,
iterator,
body,
span,
has_let,
annotations: vec![],
})
}
fn parse_block_aware_tail_expression(
pair: Pair<'_>,
diagnostics: &mut Diagnostics,
) -> Option<Expression> {
assert_correct_parser(&pair, &[Rule::block_aware_tail_expression], diagnostics);
let inner = pair
.into_inner()
.next()
.expect("block aware tail expression is not empty");
match inner.as_rule() {
Rule::expression => parse_expression(inner, diagnostics),
Rule::identifier => Some(Expression::Identifier(parse_identifier(inner, diagnostics))),
_ => {
unreachable_rule(&inner, "block_aware_tail_expression", diagnostics);
None
}
}
}
/// Lifts the error from `parse` into the top-level optional. The second level optional will
/// reflect whether there was a rule in the first place.
fn parse_optional_rule<T>(
rule: Option<Pair<'_>>,
parse: impl FnOnce(Pair<'_>) -> Option<T>,
) -> Option<Option<T>> {
rule.map_or(Some(None), |rule| parse(rule).map(Some))View on GitHub (pinned to bd85ce9dee)
Solutions
- Add a body/tail expression to the while/for/if block that triggered the crash so the rule has at least one child
- Check the BAML source around the reported span for an empty block (`{}`) or a truncated expression and complete it
- If you are a contributor: tighten the .pest rule (`block_aware_tail_expression` must require ≥1 child) or handle the empty case with diagnostics instead of `expect`
- Update baml-cli/baml to a version where this grammar/code mismatch is fixed
Example fix
// before (baml source that panics)
while input != null {}
// after
while input != null {
output = input
} Defensive patterns
Strategy: validation
Validate before calling
// Before invoking the parser (or when generating BAML programmatically):
function validateBlockHasBody(blockText) {
// every while/for/if must have a non-empty body
const m = blockText.match(/(while|for|if)\s*\([^)]*\)\s*\{\s*\}/);
if (m) throw new Error(`Empty block body will crash the BAML parser: ${blockText}`);
} Type guard
const isNonEmptyBlock = (blockText) => !/\{\s*\}\s*$/.test(blockText.trim()); Prevention
- Never emit empty while/for/if bodies when generating BAML programmatically
- When contributing, keep the .pest rule and the parser match in sync; prefer diagnostics over expect/unreachable
- Add parser fuzz/round-trip tests covering empty block bodies
- Pin baml grammar and parser crates to the same version
When it happens
Trigger: Calling `parse_block_aware_tail_expression` with a Pair whose rule is `block_aware_tail_expression` but which contains no inner pairs — i.e. the grammar accepted an empty block-aware tail expression (e.g. `while cond {}` / `for x in it {}` / `if cond {}` with no body tail in a grammar revision where the rule can match empty input).
Common situations: Developers writing BAML while/for/if blocks whose body is empty and hitting a parser crash instead of a diagnostics message; contributors who modified baml.pest so that `block_aware_tail_expression` can match zero children; running an older/newer grammar with mismatched parser code.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- sys_op callee must resolve to a statically-known global func
- exhaustive realized-leaf template classification
- interface index {} is not an Object::Interface
- interface `{}` declares no method `{}` for its default
- type-alias offset fits u32
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/049e00859ee0df09.
Report an issue: GitHub.