BoundaryML/baml · error

Expected a numeric value

Error message

Expected a numeric value

What it means

ValueExpr::resolve_numeric resolves an expression and requires the result to be ResolvedValue::Numeric, returning its textual numeric value. If the expression resolves to a non-numeric variant or fails to resolve, the method throws 'Expected a numeric value' and drops the underlying cause.

Solutions

  1. Inspect the raw resolved value via resolve() to see what type it actually is.
  2. Ensure the config value is unquoted numeric (temperature 0.7).
  3. If from an env var, set it to a plain number (TEMPERATURE=0.7) and verify it is not empty.
  4. Provide a numeric default in the config so missing env vars still resolve to Numeric.
  5. Match on resolve() at the call site for a clearer error when scalars are possible.

Example fix

// before (env TEMPERATURE=warm)
let t = expr.resolve_numeric(&ctx)?; // Err: Expected a numeric value
// after
// TEMPERATURE=0.7 (unquoted numeric) or config: temperature 0.7
let t = expr.resolve_numeric(&ctx)?;
Defensive patterns

Strategy: validation

Validate before calling

let resolved = expr.resolve(&ctx)?;
if !matches!(resolved, ResolvedValue::Numeric(_)) {
    bail!("value is not numeric: {resolved:?}");
}
let n = expr.resolve_numeric(&ctx)?;

Type guard

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

Try / catch

let n = match expr.resolve_numeric(&ctx) {
    Ok(n) => n,
    Err(_) => "0".to_string(), // safe default
};

Prevention

When it happens

Trigger: Calling resolve_numeric on an expression that resolves to a string, array, map, or null — e.g. a numeric config field (temperature, max_tokens, retries) interpolated from an unset or non-numeric env var, or quoted in config.

Common situations: Setting TEMPERATURE="warm" in the environment, quoting numbers so they resolve as strings, or leaving the env var unset so resolution falls through to a non-numeric branch.

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

Appendix: source

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

    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> {
        let value = self.resolve(ctx)?;
        let value: serde_json::Value = value.try_into()?;
        match serde_json::from_value(value) {
            Ok(v) => Ok(v),
            Err(e) => Err(anyhow::anyhow!("Failed to deserialize value: {e}")),
        }
    }

View on GitHub (pinned to bd85ce9dee)