BoundaryML/baml · error

attempted to call non-function

Error message

attempted to call non-function

What it means

After evaluating the callee of a call expression, the interpreter pattern-matches it as EvalValue::Function. If the evaluated callee is any other value (string, int, class instance, etc.) it throws "attempted to call non-function". The BAML code invoked something with call syntax `f(...)` that is not a function value.

Solutions

  1. Verify the identifier being called is actually a BAML function or fn definition, not a value
  2. Rename any variable that shadows the function name you intend to call
  3. If calling a method, ensure you access it via the correct base (e.g. obj.method()) rather than calling the object itself
  4. Check for typos in the function name

Example fix

// before
let x = "hello"
x() // attempted to call non-function
// after
fn greet() { "hello" }
greet()
Defensive patterns

Strategy: type-guard

Validate before calling

// Confirm the callee resolves to a function before calling
function assertCallable(fnName: string, knownFns: Set<string>): void {
  if (!knownFns.has(fnName)) throw new Error(`${fnName} is not a defined BAML function`);
}

Type guard

function isFunctionValue(v: unknown): v is (...args: unknown[]) => unknown {
  return typeof v === "function";
}

Try / catch

try {
  return await callBamlFn(name, args);
} catch (e) {
  if (String(e).includes("attempted to call non-function")) {
    throw new Error(`'${name}' is not a function — check for shadowing or a typo`);
  }
  throw e;
}

Prevention

When it happens

Trigger: A call expression whose func expression evaluates to a non-function value — e.g. calling a variable holding a string/int, calling a class name instead of a method, or shadowing a function name with a value.

Common situations: Shadowing a function name with a local variable; calling a field that holds a value instead of a method; typo where the intended function was never defined and a same-named value resolved instead; interpreter evaluating dynamic/macro-generated BAML.

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/55df4ff4c4f8323d. Report an issue: GitHub.

Appendix: source

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

                            return Ok(EvalValue::Value(value.clone()));
                        } else {
                            bail!("Environment variable '{}' not found", key);
                        }
                    }
                }

                let callee = evaluate_expr(
                    func,
                    scopes,
                    thir,
                    run_llm_function,
                    watch_handler,
                    function_name,
                )
                .await?;
                let (arity, body, meta) = match callee {
                    EvalValue::Function(a, b, m) => (a, b, m),
                    _ => bail!("attempted to call non-function"),
                };

                // Check if this is an LLM function call
                if let Some(Expr::Value(BamlValueWithMeta::String(marker, _))) = &body.trailing_expr
                {
                    if marker.starts_with("__LLM_FUNCTION__") {
                        let fn_name = marker.strip_prefix("__LLM_FUNCTION__").unwrap().to_string();

                        // Evaluate arguments and convert to BamlValue
                        let mut llm_args: Vec<BamlValue> = Vec::with_capacity(args.len());
                        for a in args.iter() {
                            let arg_val = expect_value(
                                evaluate_expr(
                                    a,
                                    scopes,
                                    thir,
                                    run_llm_function,
                                    watch_handler,

View on GitHub (pinned to bd85ce9dee)