BoundaryML/baml · error

ai.Prompt.messages receiver must be an ai.Prompt instance

Error message

ai.Prompt.messages receiver must be an ai.Prompt instance

What it means

Panic in `ai.Prompt.messages` when the receiver value is not an `ai.Prompt` instance at all. The method calls `vm.as_instance(prompt).expect(...)` before reading field 0, so any non-instance value passed as the method receiver aborts. This guards the implicit `self` contract of the Prompt class methods.

Source

Thrown at baml_language/crates/bex_vm/src/package_baml/prompt.rs:329

            }
        }
    }
}

impl BamlClassPrompt for PackageAiImpl {
    fn text(vm: &BexVm, prompt: &ai_view::Prompt<'_>) -> bex_str::BexStr {
        let data = prompt.instance.load_field(0);
        let prompt = vm
            .as_rust_data::<PromptAst>(&data)
            .expect("ai.Prompt._data must contain baml_builtins2::PromptAst");
        bex_str::BexStr::from(prompt.render_text())
    }

    fn messages(vm: &mut BexVm, prompt: &Value) -> Vec<Value> {
        let messages = {
            let instance = vm
                .as_instance(prompt)
                .expect("ai.Prompt.messages receiver must be an ai.Prompt instance");
            let data = instance.load_field(0);
            vm.as_rust_data::<PromptAst>(&data)
                .expect("ai.Prompt._data must contain baml_builtins2::PromptAst")
                .to_structured_messages()
        };
        let message_class = vm.resolve_class("ai.PromptMessage");
        messages
            .into_iter()
            .map(|(role, content, metadata)| {
                let role = Value::object(vm.alloc_string(role));
                let readable = Value::object(vm.alloc_string(content.to_text()));
                let parts = prompt_content_values(vm, content.as_ref());
                let parts =
                    Value::object(vm.alloc_array(bex_vm_types::RealizedTy::unknown(), parts));
                let metadata = prompt_metadata_value(vm, metadata);
                Value::object(
                    vm.alloc_instance(message_class, vec![role, readable, parts, metadata]),
                )

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Ensure the receiver is a real ai.Prompt instance: check vm.as_instance(value).is_ok() before calling messages.
  2. Invoke messages via normal BAML method-call syntax so the receiver is bound automatically.
  3. Fix any custom dispatch code that drops or substitutes the receiver argument.

Example fix

// before
let msgs = PackageAiImpl::messages(&mut vm, &maybe_prompt);
// after
if vm.as_instance(&maybe_prompt).is_err() {
    return; // receiver is not an ai.Prompt instance
}
let msgs = PackageAiImpl::messages(&mut vm, &maybe_prompt);
Defensive patterns

Strategy: type-guard

Validate before calling

fn is_prompt_instance(vm: &BexVm, value: &Value) -> bool {
    vm.as_instance(value).is_ok()
}

Type guard

fn prompt_receiver<'a>(vm: &BexVm, v: &'a Value) -> Option<&'a Instance> {
    vm.as_instance(v).ok()
}

Try / catch

// Guard the receiver before the method call:
if prompt_receiver(&vm, &value).is_none() { return; /* not an ai.Prompt */ }

Prevention

When it happens

Trigger: Calling `ai.Prompt.messages` with a value that is not a class instance (a primitive, a map, a union value) — typically from custom glue code, reflection-based dispatch, or a dispatch bug that fails to bind the receiver.

Common situations: Embedding the VM and invoking Prompt methods manually with wrong receiver values; partially applied/curried native calls where the receiver argument was dropped.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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