BoundaryML/baml · error · anyhow::Error

Unification error

Error message

Unification error

What it means

In `BamlValueWithMeta::zip_meta`, which zips two meta-annotated BAML values shape-by-shape, a `String`-vs-non-`String` pair falls into the catch-all arm `(String(_,_), _) => anyhow::bail!("Unification error")`. It means the two values being unified have different BAML types at this position, so they cannot be zipped into a single value with paired metadata.

Source

Thrown at engine/baml-lib/baml-types/src/baml_value.rs:1244

    /// The baml value calling `zip_meta` is the "primary" one, whose value
    /// data will live on in the returned baml value.
    pub fn zip_meta<U: Clone + std::fmt::Debug>(
        self,
        other: &BamlValueWithMeta<U>,
    ) -> Result<BamlValueWithMeta<(T, U)>>
    where
        T: std::fmt::Debug,
    {
        let other_meta: U = other.meta().clone();
        let error_msg = String::new();
        let ret = match (self, other) {
            (BamlValueWithMeta::Null(meta1), _) => {
                Result::<_, _>::Ok(BamlValueWithMeta::Null((meta1, other_meta)))
            }
            (BamlValueWithMeta::String(s1, meta1), BamlValueWithMeta::String(_s2, _)) if true => {
                Ok(BamlValueWithMeta::String(s1, (meta1, other_meta)))
            }
            (BamlValueWithMeta::String(_, _), _) => anyhow::bail!("Unification error"),
            (BamlValueWithMeta::Int(s1, meta1), BamlValueWithMeta::Int(_s2, _)) if true => {
                Ok(BamlValueWithMeta::Int(s1, (meta1, other_meta)))
            }
            (BamlValueWithMeta::Int(_, _), _) => anyhow::bail!("Unification error"),
            (BamlValueWithMeta::Float(s1, meta1), BamlValueWithMeta::Float(_s2, _)) if true => {
                Ok(BamlValueWithMeta::Float(s1, (meta1, other_meta)))
            }
            (BamlValueWithMeta::Float(_, _), _) => anyhow::bail!("Unification error"),
            (BamlValueWithMeta::Bool(s1, meta1), BamlValueWithMeta::Bool(_s2, _)) if true => {
                Ok(BamlValueWithMeta::Bool(s1, (meta1, other_meta)))
            }
            (BamlValueWithMeta::Bool(_, _), _) => anyhow::bail!("Unification error"),
            (BamlValueWithMeta::Map(s1, meta1), BamlValueWithMeta::Map(s2, _)) => {
                let map_result = s1
                    .into_iter()
                    .zip(s2)
                    .map(|((k1, v1), (_k2, v2))| v1.zip_meta(v2).map(|res| (k1, res)))
                    .collect::<Result<IndexMap<_, _>>>()?;

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Verify both values' types before calling zip_meta; only zip values of the same BAML type.
  2. Check where the two values diverge — typically one came from a parse of model output and the other from a schema/coercion step.
  3. If a String-vs-other unification is legitimately expected, convert one side explicitly first (e.g. parse or wrap).
  4. Capture the enclosing error_msg: zip_meta maps any failure to it, so the underlying bail is reported via that message.

Example fix

// before
let zipped = string_value.zip_meta(other_value)?; // Unification error
// after
if matches!(other_value, BamlValueWithMeta::String(..)) {
    let zipped = string_value.zip_meta(other_value)?;
} else {
    return Err(anyhow!("expected string value, got different BAML type"));
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Rust: verify both values are String-typed before zip_meta
fn can_zip_strings(a: &BamlValueWithMeta<M1, M2>, b: &BamlValueWithMeta<M1, M2>) -> bool {
    matches!(a, BamlValueWithMeta::String(..)) && matches!(b, BamlValueWithMeta::String(..))
}

Type guard

fn as_string(v: &BamlValueWithMeta<M1, M2>) -> Option<(&str,)> {
    if let BamlValueWithMeta::String(s, _) = v { Some((s,)) } else { None }
}

Try / catch

// Rust
match a.zip_meta(b) {
    Ok(zipped) => use(zipped),
    Err(e) if e.to_string().contains("Unification error") => {
        // shapes diverged: log both value types and fall back to per-type handling
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `value_a.zip_meta(value_b)` (public API) where value_a is a BamlValueWithMeta::String and value_b is Null, Int, Float, Bool, Map, List, Media, Enum, or Class.

Common situations: Comparing/streaming partial LLM output against an expected value where the model returned a string but the target value is a different type (or vice versa); merging results of two BAML runs whose shapes diverged.

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