BoundaryML/baml · error

Failed to decode BamlValue

Error message

Failed to decode BamlValue

What it means

Thrown inside BamlMethodArguments::decode when a kwargs entry has a valid string key but its `value` field is None, so Value::decode cannot be performed. The generic message does not name the key because the guard is a simple None match on the value.

Source

Thrown at engine/language_client_cffi/src/ctypes/object_args_decode.rs:38

        Ok(BamlMethodArguments {
            object: match from.object.map(RawPtrType::decode).transpose()? {
                Some(object) => object,
                None => {
                    return Err(anyhow::anyhow!("Failed to decode RawPtrType for object"));
                }
            },
            method_name: from.method_name,
            kwargs: from
                .kwargs
                .into_iter()
                .map(|v| {
                    let key = match v.key {
                        Some(crate::baml::cffi::host_map_entry::Key::StringKey(k)) => k,
                        _ => return Err(anyhow::anyhow!("Key must be a string")),
                    };
                    match v.value {
                        Some(value) => Ok((key, Value::decode(value)?)),
                        None => Err(anyhow::anyhow!("Failed to decode BamlValue")),
                    }
                })
                .collect::<Result<_, _>>()?,
        })
    }
}

impl Decode for BamlObjectConstructorArgs {
    type From = crate::baml::cffi::BamlObjectConstructorInvocation;

    fn decode(from: Self::From) -> Result<Self, anyhow::Error> {
        Ok(BamlObjectConstructorArgs {
            object_type: BamlObjectType::try_from(from.r#type)?,
            kwargs: from
                .kwargs
                .into_iter()
                .map(|v| {
                    let key = match v.key {

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Encode nulls explicitly as a null Value (per the bindings' null representation) instead of leaving the protobuf field unset
  2. Omit None-valued kwargs from the map entirely rather than passing entries without values
  3. Upgrade host bindings/runtime together so optional value serialization is consistent

Example fix

// before (Python)
kwargs = {"options": None}
client.CallMethod("m", kwargs)

// after
kwargs = {k: v for k, v in kwargs.items() if v is not None}
client.CallMethod("m", kwargs)
Defensive patterns

Strategy: validation

Validate before calling

clean_kwargs = {k: v for k, v in kwargs.items() if v is not None}
# or, if nulls are meaningful, use the bindings' explicit null value
clean_kwargs = {k: (v if v is not None else baml_null) for k, v in kwargs.items()}

Type guard

def all_kwargs_encoded(kwargs: dict) -> bool:
    return all(v is not _unset_sentinel for v in kwargs.values())

Try / catch

try:
    result = obj.CallMethod(method, kwargs)
except Exception as e:
    if "Failed to decode BamlValue" in str(e):
        raise ValueError("A kwarg had no encoded value; pass an explicit null or drop the entry") from e
    raise

Prevention

When it happens

Trigger: A method-call kwarg is present with no encoded value — e.g. passing None/null values that the serializer drops instead of encoding as an explicit null Value.

Common situations: Calling BAML object methods with None-valued parameters, bindings that serialize absent optional fields by omission, or hand-built HostMapEntry objects in tests missing the value.

Understand the failure class

Related errors


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