{"record":{"id":"3547b956b029b677","repo":"BoundaryML/baml","slug":"encountered-impossible-jinja-expression-during-parsing","errorCode":null,"errorMessage":"Encountered impossible jinja expression during parsing","messagePattern":"Encountered impossible jinja expression during parsing","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/baml-lib/ast/src/parser/parse_expression.rs","lineNumber":637,"sourceCode":"                Expression::JinjaExpressionValue(\n                    JinjaExpression(inner_text),\n                    diagnostics.span(token.as_span()),\n                )\n            }\n            _ => {\n                unreachable_rule(&token, \"jinja_expression\", diagnostics);\n                Expression::JinjaExpressionValue(\n                    JinjaExpression(String::new()),\n                    diagnostics.span(token.as_span()),\n                )\n            }\n        })\n        .next();\n\n    if let Some(value) = value {\n        value\n    } else {\n        unreachable!(\"Encountered impossible jinja expression during parsing\")\n    }\n}\n\npub fn parse_class_constructor(token: Pair<'_>, diagnostics: &mut Diagnostics) -> Expression {\n    assert_correct_parser(&token, &[Rule::class_constructor], diagnostics);\n\n    let span = diagnostics.span(token.as_span());\n    let mut tokens = token.into_inner();\n\n    let ident_token = tokens.next().expect(\"Guaranteed by the grammar\");\n\n    let class_name = match ident_token.as_rule() {\n        Rule::identifier => parse_identifier(ident_token, diagnostics),\n        Rule::path_identifier => parse_path_identifier(ident_token, diagnostics),\n        _ => panic!(\"Encountered impossible class constructor during parsing\"),\n    };\n\n    let mut fields = Vec::new();","sourceCodeStart":619,"sourceCodeEnd":655,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-lib/ast/src/parser/parse_expression.rs#L619-L655","documentation":"This is an internal parser invariant panic in BAML's AST parser. After parsing the inner tokens of a jinja expression ({{ ... }}), the code assumes the grammar always yields exactly one value expression; if none was produced it calls unreachable!(). Hitting it means the pest grammar and the Rust parser code disagree — a bug in the library, not user input the parser was designed to reject.","triggerScenarios":"Calling parse_jinja_expression with a Rule::jinja_expression pair whose inner token stream yields no expression after token filtering — i.e. an empty or malformed inner content that nevertheless matched the grammar rule.","commonSituations":"Developers hit this when using a BAML file with jinja-style {{ }} expressions after upgrading BAML versions where the grammar changed, or when the parser's token filter (.filter / map chain) drops the only child token. Effectively only reachable through library development or a genuine parser bug.","solutions":["Check the .bml source at the reported span for an empty or degenerate {{ }} expression and fix the input","Report the input snippet to the BAML repository (bug in grammar/parser mismatch)","Pin/downgrade to a previous BAML version where the jinja grammar matched the parser","Rebuild the parser to ensure the grammar .pest file and generated code are in sync"],"exampleFix":"// before (input)\nprompt {{}}\n// after\nprompt {{ name }}","handlingStrategy":"validation","validationCode":"// Pre-scan .bml prompt text for empty jinja expressions before parsing\nfn has_empty_jinja(src: &str) -> bool {\n    src.match_indices(\"{{\").any(|(i, _)| {\n        let rest = &src[i + 2..];\n        match rest.find(\"}}\") {\n            Some(j) => rest[..j].trim().is_empty(),\n            None => true,\n        }\n    })\n}\nif has_empty_jinja(source) { panic!(\"fix empty {{ }} before parsing\"); }","typeGuard":"fn is_valid_jinja(expr: &str) -> bool {\n    let inner = expr.trim().trim_start_matches(\"{{\").trim_end_matches(\"}}\").trim();\n    !inner.is_empty()\n}","tryCatchPattern":"// Panic (not Result) — isolate parsing in a subprocess/catch_unwind\nlet result = std::panic::catch_unwind(|| baml_ast::parse(source));\nmatch result {\n    Ok(ast) => use_ast(ast),\n    Err(_) => report_parser_bug(source),\n}","preventionTips":["Never ship {{ }} with empty content in .bml prompt templates","Keep BAML CLI/library and grammar versions in sync","Run `baml fmt` or the CLI check to surface syntax problems as diagnostics before library parsing","Include the offending .bml snippet when filing parser bugs"],"tags":["parser","rust","panic","jinja","internal-bug"],"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-14T11:17:12.474Z"}