BoundaryML/baml · error

for loop requires iterable (list)

Error message

for loop requires iterable (list)

What it means

The BAML interpreter's for-loop handler only supports iterating over a list value. When the loop expression evaluates to any other type (int, string, map, null, etc.), handle_statement bails with this error. It is a runtime type restriction of the for-in construct in the interpreted language.

Solutions

  1. Ensure the loop expression evaluates to a list (e.g. wrap a single value in [value]).
  2. If iterating a map, iterate over Map.keys() or entries instead.
  3. Check for null/optional returns from functions called in the loop expression and handle them before looping.

Example fix

// before
for k in myMap {
  ...
}
// after
for k in myMap.keys() {
  ...
}
Defensive patterns

Strategy: type-guard

Validate before calling

if !isList(loopExpr) { throw new Error("for loop expects a list, got: " + typeName(loopExpr)); }

Type guard

function isList(v) { return Array.isArray(v); }

Try / catch

try { evaluateBlock(block, scopes); } catch (e) { if (e.message.includes('for loop requires iterable')) { /* handle non-iterable loop target */ } else { throw e; } }

Prevention

When it happens

Trigger: Executing `for x in <expr> { ... }` where <expr> evaluates to a non-list BamlValue (e.g. an int, string, map, or null) at runtime.

Common situations: Iterating over a map instead of map.keys()/values, forgetting a function returns an optional/null, or assuming a string is iterable character-by-character.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

                                            scopes.pop();
                                            return Ok(Some(ControlFlow::Break));
                                        }
                                        ControlFlow::Continue => {
                                            scopes.pop();
                                            continue;
                                        }
                                        ControlFlow::Normal(_) => {
                                            scopes.pop();
                                        }
                                        ControlFlow::Return(val) => {
                                            scopes.pop();
                                            scopes.pop();
                                            return Ok(Some(ControlFlow::Return(val)));
                                        }
                                    }
                                }
                            }
                            _ => bail!("for loop requires iterable (list)"),
                        }
                    }
                    Statement::AssignOp {
                        left,
                        value,
                        assign_op,
                        ..
                    } => {
                        use crate::hir::AssignOp;

                        let current_val = expect_value(
                            evaluate_expr(
                                left,
                                scopes,
                                thir,
                                run_llm_function,
                                watch_handler,
                                function_name,

View on GitHub (pinned to bd85ce9dee)