BoundaryML/baml · error

! operator requires boolean operand at

Error message

! operator requires boolean operand at {:?}

What it means

Thrown when the unary `!` (logical not) operator is applied to a value that is not a Bool at runtime. The interpreter only implements Not for BamlValueWithMeta::Bool; all other variants bail. BAML does not implicitly truthiness-coerce values like JavaScript does.

Solutions

  1. Ensure the operand of ! is a boolean expression; add an explicit comparison, e.g. `!(x == 0)` instead of `!x`
  2. Annotate the variable as bool so the typechecker catches non-bool values before interpretation
  3. Convert truthiness manually, e.g. `x == null` or `x == ""` rather than `!x`
  4. If the value is optional, unwrap/coalesce it to a bool first (`x ?? false`)

Example fix

// before (BAML)
let disabled = !retries;   // retries: int
// after
let disabled = !(retries > 0);
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure the operand is boolean before negation
if (typeof flag !== 'boolean') {
  throw new Error(`! requires bool, got ${typeof flag}`);
}

Type guard

const isBool = (v: unknown): v is boolean => typeof v === 'boolean';

Try / catch

try {
  result = evaluate(expr);
} catch (e) {
  if (String(e).includes('! operator requires boolean operand')) {
    // rewrite as explicit comparison
  }
  throw e;
}

Prevention

When it happens

Trigger: Evaluating `!x` where `x` is an int, float, string, null, list, or class instance instead of a bool. Common with values flowing from JSON parsing, optional fields, or function returns typed loosely.

Common situations: Negating a comparison result that was accidentally omitted (`!x` instead of `!(x == y)`), negating an optional/int flag from config, or applying ! to a string in a prompt-expression.

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

Appendix: source

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

        BinaryOperator::InstanceOf => match (left_val.clone(), right_val.clone()) {
            (BamlValueWithMeta::Class(class, ..), BamlValueWithMeta::Class(right_class, ..)) => {
                BamlValueWithMeta::Bool(class == right_class, meta.clone())
            }
            _ => bail!("instanceof requires class operands at {:?}", meta.0),
        },
    })
}

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,

View on GitHub (pinned to bd85ce9dee)