BoundaryML/baml · error

ai.Prompt._data must contain baml_builtins2::PromptAst

Error message

ai.Prompt._data must contain baml_builtins2::PromptAst

What it means

Panic in `ai.Prompt.text` when the Prompt instance's field 0 (`_data`) does not hold Rust data of type `baml_builtins2::PromptAst`. The method unconditionally downcasts the field to render the prompt as text; a failed downcast means the instance was constructed with wrong field layout or wrong data type, breaking the ai.Prompt class invariant.

Source

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

                && let Some(&new_ptr) = forwarding.get(&ptr)
            {
                *value = Value::object(new_ptr);
            }
        }
        for ptr in &mut self.pending {
            if let Some(&new_ptr) = forwarding.get(ptr) {
                *ptr = new_ptr;
            }
        }
    }
}

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));

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Construct ai.Prompt only through its official constructor so field 0 receives a PromptAst.
  2. Check that the Prompt class field layout (field 0 = _data) matches the VM's registered class definition — rebuild all crates together.
  3. Verify the value is a PromptAst before rendering: vm.as_rust_data::<PromptAst>(&data).is_ok().
  4. Report the misconstructed instance path to baml_language maintainers.

Example fix

// before
let prompt = PackageAiImpl::text(&vm, &p);
// after
if vm.as_rust_data::<PromptAst>(&p.instance.load_field(0)).is_err() {
    return; // or surface a proper error instead of panicking
}
let prompt = PackageAiImpl::text(&vm, &p);
Defensive patterns

Strategy: type-guard

Validate before calling

fn is_prompt_ast(vm: &BexVm, value: &Value) -> bool {
    vm.as_instance(value)
        .map(|i| vm.as_rust_data::<PromptAst>(&i.load_field(0)).is_ok())
        .unwrap_or(false)
}

Type guard

fn as_prompt<'a>(vm: &BexVm, v: &'a Value) -> Option<&'a PromptAst> {
    vm.as_instance(v)
        .ok()
        .and_then(|i| vm.as_rust_data::<PromptAst>(&i.load_field(0)).ok())
}

Try / catch

// Panic cannot be caught; guard before rendering:
if as_prompt(&vm, &value).is_none() { /* surface proper error */ }

Prevention

When it happens

Trigger: Constructing an `ai.Prompt` instance whose field 0 is not a PromptAst rust-data value — e.g. via reflection/raw value construction, a class-layout mismatch after VM upgrades, or passing a foreign instance into `ai.Prompt.text`.

Common situations: Embedding the VM and building Prompt instances manually, version skew between code that constructs Prompt instances and the prompt package's expected layout, or corrupted serialized class instances.

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