BoundaryML/baml · error

Key must be a string

Error message

Key must be a string

What it means

Thrown by from_host_map_entry when a HostMapEntry's key oneof is not StringKey (it is IntKey, BoolKey, EnumKey, or unset). The plain CFFI Value decoder only supports string keys, so any other key type is rejected with this fixed message.

Source

Thrown at engine/language_client_cffi/src/ctypes/cffi_value_decode.rs:54

                    .map(from_host_map_entry)
                    .collect::<Result<_, _>>()?;
                Value::Class(c.name, fields, ())
            }
            HostVal::EnumValue(e) => Value::Enum(e.name, e.value, ()),
            HostVal::Handle(handle) => {
                let raw_ptr = RawPtrType::decode(handle)?;
                Value::RawPtr(raw_ptr, ())
            }
        })
    }
}

pub(super) fn from_host_map_entry(
    item: crate::baml::cffi::HostMapEntry,
) -> Result<(String, Value), anyhow::Error> {
    let key = match item.key {
        Some(crate::baml::cffi::host_map_entry::Key::StringKey(k)) => k,
        _ => return Err(anyhow::anyhow!("Key must be a string")),
    };
    let value = item
        .value
        .ok_or(anyhow::anyhow!("Value is null for key {}", key))?;
    Ok((key, Value::decode(value)?))
}

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Convert all map keys to strings on the host side before passing them to the BAML client (e.g. str(key) in Python)
  2. If non-string keys are semantically needed, encode them as strings and decode host-side after the call
  3. Check for JSON round-trips that convert string keys to numeric ones and normalize keys after parsing

Example fix

// before (Python)
kwargs = {1: "one"}

// after
kwargs = {str(k): v for k, v in kwargs.items()}
Defensive patterns

Strategy: validation

Validate before calling

def stringify_keys(d):
    if not all(isinstance(k, str) for k in d):
        raise TypeError("All map keys must be strings")
    return {str(k): v for k, v in d.items()}

kwargs = stringify_keys(raw_kwargs)

Type guard

def has_string_keys(d: dict) -> bool:
    return all(isinstance(k, str) for k in d.keys())

Try / catch

try:
    result = client.CallFunction(fn, kwargs)
except Exception as e:
    if "Key must be a string" in str(e):
        kwargs = {str(k): v for k, v in kwargs.items()}
        result = client.CallFunction(fn, kwargs)
    else:
        raise

Prevention

When it happens

Trigger: Passing a map/dict with non-string keys (int or bool keys) from the host language into a BAML function call that decodes to a Value map.

Common situations: Python/JS developers passing {1: "x"} or {True: "y"} as kwargs or map parameters; language defaults that produce integer keys from JSON-like literals.

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


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