{"record":{"id":"57c7f88029b6bb71","repo":"BoundaryML/baml","slug":"unknown-variable","errorCode":null,"errorMessage":"Unknown variable {}","messagePattern":"Unknown variable (.+?)","errorType":"validation","errorClass":"DatamodelError","httpStatus":null,"severity":"error","filePath":"engine/baml-lib/baml-core/src/validate/validation_pipeline/validations/expr_fns.rs","lineNumber":44,"sourceCode":"    let builtin_classes = [ir::builtin::classes::REQUEST];\n\n    HashSet::from_iter(\n        builtin_functions\n            .iter()\n            .chain(builtin_classes.iter())\n            .map(ToString::to_string),\n    )\n}\n\n/// Validate that the left-hand side of an assignment refers to a declared variable\n/// when it is a bare identifier. This is used to enforce that C-style for-loop\n/// headers either declare their iterator with `let` in the init statement, or\n/// use a variable already declared in the containing scope.\nfn validate_assign_lhs_in_scope(ctx: &mut Context<'_>, left: &Expression, scope: &HashSet<String>) {\n    if let Expression::Identifier(identifier) = left {\n        if !scope.contains(&identifier.to_string()) {\n            ctx.push_error(DatamodelError::new_anyhow_error(\n                anyhow::anyhow!(\"Unknown variable {}\", &identifier.to_string()),\n                identifier.span().clone(),\n            ));\n        }\n    }\n}\n\n// An expr_fn is valid if:\n//   - Its parameters have valid types.\n//   - Its parameter names are not reserved keywords.\n//   - Its return type is valid.\n//   - Its body is a valid function body (series of statements ending in an\n//     expression). Bodies are valid if they refer only to variables defined\n//     in the parameter list and in the current scope.\n//   - It does not share a name with any other expr_fn or LLM function.\npub(super) fn validate_expr_fns(ctx: &mut Context<'_>) {\n    let mut defined_types = internal_baml_jinja_types::PredefinedTypes::default(\n        internal_baml_jinja_types::JinjaContext::Prompt,\n    );","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-lib/baml-core/src/validate/validation_pipeline/validations/expr_fns.rs#L26-L62","documentation":"During expression-function validation, an assignment's left-hand side identifier is checked against the set of variables declared in the current scope. If the identifier being assigned was never declared (no `let`) or is not otherwise in scope, the compiler pushes this error.","triggerScenarios":"Writing `x = 5;` inside a BAML expression function or block without first declaring `let x = ...`, when x is not a function parameter or outer-scope variable.","commonSituations":"Porting code from languages with implicit variable declaration; typos in variable names inside expression functions (e.g. `mesage = msg`); assigning to a variable declared in a sibling branch that is out of scope.","solutions":["Declare the variable first with `let x = ...` before assigning","Check spelling of the variable against its declaration or function parameters","Ensure the declaration is in an enclosing scope, not a sibling block that already closed","If it was meant to be a parameter, add it to the function signature"],"exampleFix":"// before\nfunction foo() {\n  total = 0 // undeclared\n}\n// after\nfunction foo() {\n  let total = 0\n}","handlingStrategy":"validation","validationCode":"// Pre-check assignment LHS is declared\nfn assign_ok(lhs: &str, scope: &HashSet<String>) -> bool {\n    scope.contains(lhs)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always declare with `let` before assigning in expression functions","Enable BAML editor diagnostics to catch undeclared identifiers while typing","Avoid reusing names across nested blocks"],"tags":["baml","validation","scope","undeclared-variable"],"backgroundTag":"invalid-identifier","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}