BoundaryML/baml · error · LlmOpError

Expected {expected}, got {actual}

Error message

Expected {expected}, got {actual}

What it means

LlmOpError::TypeError from the SAP LLM operation layer: a value received during an LLM operation did not have the expected Rust type. It is constructed by the SAP parse entry points when internal type expectations about response data are violated.

Source

Thrown at baml_language/crates/sys_ops/src/sap.rs:52

    pub fn stream_ty_resolved(
        &self,
    ) -> Result<
        sap_model::TyWithMeta<
            sap_model::TyResolvedRef<'_, DefKey>,
            &sap_model::TypeAnnotations<'_, DefKey>,
        >,
        &DefKey,
    > {
        self.db().resolve_with_meta(self.types.stream_ty().as_ref())
    }
}

/// Errors that can occur during LLM operations. Relocated verbatim from
/// `sys_llm`; only `ParseResponseError`, `JsonishError` and `SapError` are
/// still constructible now that the SAP parse entry points are the sole users.
#[derive(Debug, thiserror::Error)]
pub enum LlmOpError {
    #[error("Expected {expected}, got {actual}")]
    TypeError {
        expected: &'static str,
        actual: String,
    },

    #[error("Parse response error: {0}")]
    ParseResponseError(String),

    #[error("Jsonish error: {0}")]
    JsonishError(::bex_sap::jsonish::JsonishError),

    #[error("SAP error: {0}")]
    SapError(::bex_sap::deserializer::coercer::ParsingError),
}

impl From<LlmOpError> for ::sys_types::VmRustFnError {
    fn from(e: LlmOpError) -> Self {
        let baml: ::sys_types::VmBamlError = match e {

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Validate/repair the LLM response JSON against the expected schema before parsing
  2. Add or fix coercion so the unexpected type is converted (e.g. number to string)
  3. Check for model or prompt changes causing the shifted response shape and update the expected type

Example fix

// before
let s = value.expect_string();
// after
let s = match value { V::String(s) => s, V::Number(n) => n.to_string(), other => return Err(...) };
Defensive patterns

Strategy: try-catch

Validate before calling

fn ensure_type(v: &Value, expected: &str) -> Result<(), String> {
    match (expected, v) {
        ("string", Value::String(_)) | ("number", Value::Number(_)) | ("object", Value::Object(_)) => Ok(()),
        _ => Err(format!("expected {}, got {:?}", expected, v)),
    }
}

Type guard

fn as_string(v: &Value) -> Option<&str> { if let Value::String(s) = v { Some(s) } else { None } }

Try / catch

match result {
    Err(LlmOpError::TypeError { expected, actual }) => warn!("coerce {actual} to {expected}"),
    Err(e) => return Err(e),
    Ok(v) => v,
}

Prevention

When it happens

Trigger: A SAP parse entry point (sole remaining constructor of LlmOpError variants) encounters a value whose type doesn't match the statically expected 'expected' kind — e.g. expecting a string field but finding a number/object in the model response payload.

Common situations: LLM returning malformed or differently-typed JSON than the schema expects; drifting API response shapes after model changes; version changes in the SAP/jsonish layer.

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