BoundaryML/baml · error

undefined variable

Error message

undefined variable: {name}

What it means

The BAML compiler's code generator failed to resolve a variable name while compiling an expression. Any name must be either a local variable (compiled into LoadVar) or a global (a class/function registered in globals); if neither lookup succeeds, the compiler panics because the semantic layer should have rejected unresolved names earlier. This represents an identifier that is not in scope at the point of use.

Solutions

  1. Declare the variable before use (let or the block's parameter list) and fix any spelling mismatch with the declaration
  2. Check that the variable is defined in the same scope where it is referenced; BAML block scoping is lexical
  3. If the symbol should be a global (class or function), confirm it is defined at file scope and imported/visible in this file
  4. If this occurs with valid code, it may be a compiler bug — report it with the .baml source that triggers the panic

Example fix

// before
let prompt = "Hello " ++ usermame
// after
let prompt = "Hello " ++ username
Defensive patterns

Strategy: validation

Validate before calling

fn validate_var_defined(name: &str, locals: &HashSet<&str>, globals: &HashSet<&str>) -> Result<(), String> {
    if locals.contains(name) || globals.contains(name) { Ok(()) }
    else { Err(format!("variable '{}' is not defined in scope", name)) }
}

Type guard

fn is_defined(name: &str, scope: &Scope) -> bool { scope.locals.contains_key(name) || scope.globals.contains_key(name) }

Prevention

When it happens

Trigger: Compiling a BAML expression that references an identifier never declared (typo, use-before-declaration, reference to a symbol from another scope, or a variable only defined inside a branch the compiler path skipped). The name is absent from both self.locals and self.globals in compile_expression.

Common situations: Typos in variable names inside expression blocks; referencing a function parameter that was removed or renamed; using a variable defined in a different block scope; mid-refactor states where a declaration was deleted but uses remain.

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


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/28e6e55431cec650. Report an issue: GitHub.

Appendix: source

Thrown at engine/baml-compiler/src/codegen.rs:1213

                        };

                        self.emit(Instruction::LoadField(field_index));
                    }

                    other => panic!(
                        "field access must be on classes, but expr `{}` got: {other:?}",
                        base.dump_str()
                    ),
                }
            }

            thir::Expr::Var(name, _) => {
                if let Some(&index) = self.locals.get(name) {
                    self.emit(Instruction::LoadVar(index));
                } else if let Some(class) = self.globals.get(name) {
                    self.emit(Instruction::LoadGlobal(*class));
                } else {
                    panic!("undefined variable: {name}");
                }
            }

            thir::Expr::List(elements, _) => {
                for element in elements {
                    self.compile_expression(element);
                }
                self.emit(Instruction::AllocArray(elements.len()));
            }

            thir::Expr::Map(pairs, _) => {
                // Maps are not yet implemented in bytecode
                // have N keys, N values.
                // keys are popped first, so we first compute the values.

                for (_, value) in pairs {
                    self.compile_expression(value);
                }

View on GitHub (pinned to bd85ce9dee)