{"record":{"id":"049e00859ee0df09","repo":"BoundaryML/baml","slug":"block-aware-tail-expression-is-not-empty","errorCode":null,"errorMessage":"block aware tail expression is not empty","messagePattern":"block aware tail expression is not empty","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/baml-lib/ast/src/parser/parse_expr.rs","lineNumber":298,"sourceCode":"        identifier,\n        iterator,\n        body,\n        span,\n        has_let,\n        annotations: vec![],\n    })\n}\n\nfn parse_block_aware_tail_expression(\n    pair: Pair<'_>,\n    diagnostics: &mut Diagnostics,\n) -> Option<Expression> {\n    assert_correct_parser(&pair, &[Rule::block_aware_tail_expression], diagnostics);\n\n    let inner = pair\n        .into_inner()\n        .next()\n        .expect(\"block aware tail expression is not empty\");\n\n    match inner.as_rule() {\n        Rule::expression => parse_expression(inner, diagnostics),\n        Rule::identifier => Some(Expression::Identifier(parse_identifier(inner, diagnostics))),\n        _ => {\n            unreachable_rule(&inner, \"block_aware_tail_expression\", diagnostics);\n            None\n        }\n    }\n}\n\n/// Lifts the error from `parse` into the top-level optional. The second level optional will\n/// reflect whether there was a rule in the first place.\nfn parse_optional_rule<T>(\n    rule: Option<Pair<'_>>,\n    parse: impl FnOnce(Pair<'_>) -> Option<T>,\n) -> Option<Option<T>> {\n    rule.map_or(Some(None), |rule| parse(rule).map(Some))","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-lib/ast/src/parser/parse_expr.rs#L280-L316","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before (baml source that panics)\nwhile input != null {}\n// after\nwhile input != null {\n  output = input\n}","handlingStrategy":"validation","validationCode":"// Before invoking the parser (or when generating BAML programmatically):\nfunction validateBlockHasBody(blockText) {\n  // every while/for/if must have a non-empty body\n  const m = blockText.match(/(while|for|if)\\s*\\([^)]*\\)\\s*\\{\\s*\\}/);\n  if (m) throw new Error(`Empty block body will crash the BAML parser: ${blockText}`);\n}","typeGuard":"const isNonEmptyBlock = (blockText) => !/\\{\\s*\\}\\s*$/.test(blockText.trim());","tryCatchPattern":null,"preventionTips":["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"],"tags":["parser","panic","internal-invariant","rust"],"backgroundTag":"internal-invariant-violation","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}