BoundaryML/baml · error

Expected a map

Error message

Expected a map

What it means

ValueExpr::resolve_map resolves an expression and requires the result to be ResolvedValue::Map. Any other resolved variant (or a resolution failure whose message is dropped) yields the generic error 'Expected a map'. The underlying resolve() error is not chained, so the real cause is hidden.

Source

Thrown at engine/baml-lib/baml-types/src/value_expr.rs:562

    pub fn resolve_bool(&self, ctx: &impl GetEnvVar) -> Result<bool> {
        match self.resolve(ctx) {
            Ok(ResolvedValue::Bool(b, ..)) => Ok(b),
            _ => Err(anyhow::anyhow!("Expected a boolean")),
        }
    }

    pub fn resolve_array(&self, ctx: &impl GetEnvVar) -> Result<Vec<ResolvedValue>> {
        match self.resolve(ctx) {
            Ok(ResolvedValue::Array(a, ..)) => Ok(a),
            _ => Err(anyhow::anyhow!("Expected an array")),
        }
    }

    pub fn resolve_map(&self, ctx: &impl GetEnvVar) -> Result<IndexMap<String, ResolvedValue>> {
        match self.resolve(ctx) {
            Ok(ResolvedValue::Map(m, ..)) => Ok(m.into_iter().map(|(k, (_, v))| (k, v)).collect()),
            _ => Err(anyhow::anyhow!("Expected a map")),
        }
    }

    pub fn resolve_numeric(&self, ctx: &impl GetEnvVar) -> Result<String> {
        match self.resolve(ctx) {
            Ok(ResolvedValue::Numeric(n, ..)) => Ok(n),
            _ => Err(anyhow::anyhow!("Expected a numeric value")),
        }
    }

    pub fn resolve_null(&self, ctx: &impl GetEnvVar) -> Result<()> {
        match self.resolve(ctx) {
            Ok(ResolvedValue::Null(..)) => Ok(()),
            _ => Err(anyhow::anyhow!("Expected a null value")),
        }
    }

    pub fn resolve_serde<T: serde::de::DeserializeOwned>(&self, ctx: &impl GetEnvVar) -> Result<T> {

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Resolve the expression with resolve() to see the actual variant and the real failure cause.
  2. Ensure the config value is an object/map (JSON object syntax: {"k": "v"}).
  3. If sourced from an env var, verify the variable is set and contains a JSON object.
  4. Add a fallback/default map in config when the value may be absent.
  5. Handle the error with context: match on resolve() yourself to surface the inner message.

Example fix

// before (env PARAMS unset)
let m = expr.resolve_map(&ctx)?; // Err: Expected a map
// after
// PARAMS='{"temperature":0.7}' or config default: {"temperature": 0.7}
let m = expr.resolve_map(&ctx)?;
Defensive patterns

Strategy: validation

Validate before calling

let resolved = expr.resolve(&ctx)?;
if !matches!(resolved, ResolvedValue::Map(_)) {
    bail!("value is not a map: {resolved:?}");
}
let m = expr.resolve_map(&ctx)?;

Type guard

fn is_map(v: &ResolvedValue) -> bool { matches!(v, ResolvedValue::Map(..)) }

Try / catch

let m = expr.resolve_map(&ctx).unwrap_or_else(|_| IndexMap::new());

Prevention

When it happens

Trigger: Calling resolve_map on an expression that evaluates to a list, string, numeric, or null — e.g. a map-valued config field sourced from an env var that is unset or holds non-object JSON.

Common situations: Config sections expected to be key/value objects (params, metadata, headers) where the user supplied a scalar, or the interpolating env var is missing.

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