BoundaryML/baml · error

method calls must be identifiers

Error message

method calls must be identifiers

What it means

Method-call syntax requires the method component to be a plain identifier (Expr::Var). If the parser produced a method position holding any other expression, the code generator panics, since it constructs the dispatched name as '{ClassName}.{method}' and needs a literal method name.

Solutions

  1. Rewrite the call using standard method syntax with a literal method name (receiver.method(args))
  2. Verify the .baml source contains a normal identifier after the dot
  3. If valid source triggers this, it is a parser/codegen bug — report it with the input
  4. Check for macros or code generation steps emitting non-identifier method positions
Defensive patterns

Strategy: validation

Validate before calling

fn validate_method_call(mc: &MethodCall) -> Result<(), String> {
    match mc.method.as_ref() { Expr::Var(_, _) => Ok(()), _ => Err("method position must be an identifier".into()) }
}

Type guard

fn is_identifier_method(mc: &MethodCall) -> bool { matches!(mc.method.as_ref(), Expr::Var(_, _)) }

Prevention

When it happens

Trigger: Compiling a MethodCall expression whose method field is not Expr::Var — e.g. computed method names or nested call results in the method position. Usually only reachable via malformed AST or exotic generated code, since normal syntax cannot express this.

Common situations: Mostly compiler-internal: hand-edited or generated THIR, parser/codegen desync bugs, or fuzzed inputs. Rarely hit by hand-written BAML.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

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

                    } else {
                        args.len()
                    };

                    self.emit(Instruction::DispatchFuture(count));
                    self.emit(Instruction::Await);
                } else {
                    self.emit(Instruction::Call(args.len()));
                }
            }

            thir::Expr::MethodCall {
                receiver,
                method,
                args,
                ..
            } => {
                let thir::Expr::Var(method, _) = method.as_ref() else {
                    panic!("method calls must be identifiers");
                };

                let func_name = match receiver.meta().1.as_ref() {
                    Some(TypeIR::Class {
                        name: class_name, ..
                    }) => format!("{class_name}.{method}"),

                    Some(TypeIR::List(_, _)) => format!("baml.Array.{method}"),

                    Some(TypeIR::Map(_, _, _)) => format!("baml.Map.{method}"),

                    Some(TypeIR::Primitive(TypeValue::String, _)) => {
                        format!("baml.String.{method}")
                    }

                    Some(TypeIR::Primitive(TypeValue::Float, _))
                    | Some(TypeIR::Primitive(TypeValue::Int, _)) => {
                        format!("baml.Float.{method}")

View on GitHub (pinned to bd85ce9dee)