BoundaryML/baml · error

Map.set: should be dispatched via __glue_set

Error message

Map.set: should be dispatched via __glue_set

What it means

`Map.set` in the BAML map package is a placeholder that always panics: mutation of maps must go through the `__glue_set` native glue method, which holds the VM borrow needed to write into the map. The direct `set` binding exists only to satisfy the native-function table shape; calling it directly is a dispatch bug.

Solutions

  1. Fix the dispatch table so `Map.set` resolves to `__glue_set`.
  2. In BAML code, use map mutation syntax (map[key] = value) rather than any direct native call path.
  3. Rebuild/upgrade bex_vm if a released build shows this panic.

Example fix

// before
"set" => NativeFn::Raw(map::set),
// after
"set" => NativeFn::Glue(map::__glue_set),
Defensive patterns

Strategy: validation

Validate before calling

// Dispatch-table sanity check at map package registration:
assert!(dispatch_table.get("set") == Some(&NativeFn::Glue(map::__glue_set)));

Try / catch

// Panics are not catchable in Rust; verify registration in a startup unit test.

Prevention

When it happens

Trigger: The VM's method dispatch table routes `Map.set` to the raw `set` function instead of `__glue_set`, typically due to a misregistered native method name in the map package registration code.

Common situations: VM development or patches that alter the map package registration; custom glue dispatch layers that call native functions by name without applying the __glue_ prefix rules.

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

Appendix: source

Thrown at baml_language/crates/bex_vm/src/package_baml/map.rs:60

        let result: NativeFunctionResult = (|| {
            let key_as_string = vm.as_string(&args[1])?.clone();
            let value = args[2];
            let mut map = vm.as_map_mut(&args[0])?;
            // `IndexMap::insert` returns `Some(prev)` if the key already
            // existed, `None` otherwise — exactly the V? semantics we want.
            Ok(match map.insert(key_as_string, value) {
                Some(prev) => prev,
                None => Value::NULL,
            })
        })();
        match result {
            Ok(v) => NativeCallResult::Done(v),
            Err(e) => NativeCallResult::Error(e),
        }
    }

    fn set(_map: &mut IndexMap<BexStr, Value>, _key: &Value, _value: &Value) -> Option<Value> {
        unreachable!("Map.set: should be dispatched via __glue_set")
    }

    fn get(vm: &BexVm, map: MapView<'_>, key: &Value) -> Option<Value> {
        if let Ok(k) = vm.as_string(key) {
            map.get(k.as_str()).copied()
        } else {
            None
        }
    }

    // ── delete ────────────────────────────────────────────────────────────────

    fn __glue_delete(vm: &mut BexVm, args: &[Value]) -> NativeCallResult {
        let result: NativeFunctionResult = (|| {
            let key_as_string = vm.as_string(&args[1])?.clone();
            let mut map = vm.as_map_mut(&args[0])?;
            // `shift_remove` preserves the order of remaining entries (matching
            // insertion order) — important since `keys()` / `values()` return

View on GitHub (pinned to bd85ce9dee)