BoundaryML/baml · error

assign to undeclared variable

Error message

assign to undeclared variable `{}`

What it means

The BAML interpreter's `assign` helper walks the active scopes and, when no scope contains a cell for the variable name, bails with `assign to undeclared variable \`{name}\``. BAML requires variables to be declared (e.g. via `let` / parameter bindings) before they can be reassigned, so assignment to a never-declared name is a hard runtime error in the interpreted THIR evaluator.

Solutions

  1. Declare the variable before assigning to it (e.g. `let x = ...`) so a scope cell exists
  2. Check the variable name for typos against the declaration
  3. If the value should be produced by an expression, bind it with `let` instead of assigning
  4. Ensure the assignment is not executed in a scope where the declaration is not visible (e.g. inside a different function/block)

Example fix

// before (BAML)
let count = 0;
count = count + 1; // ok
totla = count;     // assign to undeclared variable `totla`
// after
let count = 0;
count = count + 1;
let total = count;
Defensive patterns

Strategy: validation

Validate before calling

// BAML: ensure the target is declared before assigning
let message = "";   // declaration creates the scope cell
message = "hello";  // now safe

Prevention

When it happens

Trigger: Executing a `x = value` assignment (via `assign_to_expr` on a plain `Expr::Var` target) where `x` was never introduced by a declaration, function parameter, or for-loop binding in any active scope.

Common situations: Typo in a variable name on the left side of an assignment (e.g. `mesage = ...` after `let message = ...`); attempting to create a variable by plain assignment in a BAML expression block as if it were Python/JS; using a variable defined in an inner scope after it closed.

Related errors


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

Appendix: source

Thrown at engine/baml-compiler/src/thir/interpret.rs:1447

fn declare_with_cell(
    scopes: &mut [Scope],
    name: &str,
    cell: Arc<Mutex<BamlValueWithMeta<ExprMetadata>>>,
) {
    if let Some(scope) = scopes.last_mut() {
        scope.variables.insert(name.to_string(), cell);
    }
}

fn assign(scopes: &mut [Scope], name: &str, value: BamlValueWithMeta<ExprMetadata>) -> Result<()> {
    for s in scopes.iter_mut().rev() {
        if let Some(cell) = s.variables.get_mut(name) {
            *cell.lock().unwrap() = value;
            return Ok(());
        }
    }
    bail!("assign to undeclared variable `{}`", name)
}

async fn assign_to_expr<F, Fut>(
    target: &Expr<ExprMetadata>,
    new_value: BamlValueWithMeta<ExprMetadata>,
    scopes: &mut Vec<Scope>,
    thir: &THir<ExprMetadata>,
    run_llm_function: &mut F,
    watch_handler: &SharedWatchHandler,
    function_name: &str,
) -> Result<()>
where
    F: LlmHandler<Fut>,
    Fut: LlmFuture,
{
    let mut current_expr = target;
    let mut value_to_assign = new_value;

View on GitHub (pinned to bd85ce9dee)