BoundaryML/baml · error

- operator requires numeric operand at

Error message

- operator requires numeric operand at {:?}

What it means

Thrown when the unary minus `-` operator is applied to a value that is neither an Int nor a Float at runtime. The interpreter implements Neg only for numeric BamlValue variants; strings, bools, nulls, and collections hit the bail. There is no implicit numeric coercion in BAML expressions.

Solutions

  1. Ensure the operand is numeric; convert string numbers at the source (parse to int/float when deserializing)
  2. Annotate the variable as int or float so the typechecker rejects non-numeric values
  3. Guard with a null/type check before negating optional values
  4. If the string holds a number, change the field's declared type from string to int/float in the schema

Example fix

// before (BAML)
let offset = -threshold;  // threshold: string
// after
let offset = -(threshold as int);  // or declare threshold as int
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure numeric operand before unary minus
if (typeof v !== 'number') {
  throw new Error(`- requires numeric operand, got ${typeof v}`);
}

Type guard

const isNumber = (v: unknown): v is number => typeof v === 'number' && !Number.isNaN(v);

Try / catch

try {
  result = evaluate(expr);
} catch (e) {
  if (String(e).includes('- operator requires numeric operand')) {
    // parse string to number at the source
  }
  throw e;
}

Prevention

When it happens

Trigger: Evaluating `-x` where `x` is a string (e.g. a numeric string from JSON or user input), a bool, null, or a media/class value. Frequently when a number arrives as a string via JSON deserialization into a String-typed field.

Common situations: Negating a value parsed from text/json where the declared type is string, applying negation to a config value read as string, or a typo where the operand was reassigned to a non-numeric type.

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

Appendix: source

Thrown at engine/baml-compiler/src/thir/interpret.rs:2551

        },
    })
}

fn evaluate_unary_op(
    operator: &crate::hir::UnaryOperator,
    val: &BamlValueWithMeta<ExprMetadata>,
    meta: &ExprMetadata,
) -> Result<BamlValueWithMeta<ExprMetadata>> {
    use crate::hir::UnaryOperator;
    Ok(match operator {
        UnaryOperator::Not => match val.clone() {
            BamlValueWithMeta::Bool(b, _) => BamlValueWithMeta::Bool(!b, meta.clone()),
            _ => bail!("! operator requires boolean operand at {:?}", meta.0),
        },
        UnaryOperator::Neg => match val.clone() {
            BamlValueWithMeta::Int(i, _) => BamlValueWithMeta::Int(-i, meta.clone()),
            BamlValueWithMeta::Float(f, _) => BamlValueWithMeta::Float(-f, meta.clone()),
            _ => bail!("- operator requires numeric operand at {:?}", meta.0),
        },
    })
}

fn values_equal(
    left: &BamlValueWithMeta<ExprMetadata>,
    right: &BamlValueWithMeta<ExprMetadata>,
) -> bool {
    match (left, right) {
        (BamlValueWithMeta::Int(a, _), BamlValueWithMeta::Int(b, _)) => a == b,
        (BamlValueWithMeta::Float(a, _), BamlValueWithMeta::Float(b, _)) => a == b,
        (BamlValueWithMeta::Int(a, _), BamlValueWithMeta::Float(b, _)) => *a as f64 == *b,
        (BamlValueWithMeta::Float(a, _), BamlValueWithMeta::Int(b, _)) => *a == *b as f64,
        (BamlValueWithMeta::String(a, _), BamlValueWithMeta::String(b, _)) => a == b,
        (BamlValueWithMeta::Null(_), BamlValueWithMeta::Null(_)) => true,
        _ => false,
    }
}

View on GitHub (pinned to bd85ce9dee)