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
- Verify the identifier being called is actually a BAML function or fn definition, not a value
- Rename any variable that shadows the function name you intend to call
- If calling a method, ensure you access it via the correct base (e.g. obj.method()) rather than calling the object itself
- 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
- Never reuse function names for variables in .baml files
- Rely on the static checker/LSP to flag non-function call targets
- Review call sites after converting a function to a value or vice versa
- Search the .baml file for duplicate declarations of the same name
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
- array access on non-list at
- array assignment on non-list value at
- bitwise ^= requires integer operands
- bitwise |= requires integer operands
- bitwise & requires integer operands at
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)