BoundaryML/baml · error

interface `{iface_wire}` declares a non-runtime constraint:

Error message

interface `{iface_wire}` declares a non-runtime constraint: {e:?}

What it means

In `build_interface_def`, generic-argument and constraint types of an interface are converted to runtime types with `lower_to_runtime`, and any failure triggers this `unreachable!`. It asserts that every constraint (bound / `requires` target / associated-type bound) on an interface has a runtime representation once holes have been rejected.

Source

Thrown at baml_language/crates/baml_compiler2_emit/src/lib.rs:162

                       store: &TypeRefStore,
                       id: TypeRefId|
     -> Option<bex_vm_types::RuntimeInterface> {
        // ConstraintHead, per the contract above: the default (existential)
        // position eagerly realizes omitted associated defaults and fills
        // the rest with Error sentinels — a bound like `type A extends
        // Iface` with unpinned associated types would panic `to_runtime`.
        let lowered = baml_compiler2_hir_ty::lower::reject_holes(&ctx.lower_type_ref_at(
            store,
            id,
            baml_compiler2_hir_ty::lower::TypePosition::ConstraintHead,
        ));
        let baml_type::Ty::Interface(qtn, args, assoc, _) = lowered else {
            return None;
        };
        let to_runtime = |t: &baml_type::Ty| {
            baml_type::lower_to_runtime(t, resolved.aliases)
                .unwrap_or_else(|e| {
                    unreachable!(
                        "interface `{iface_wire}` declares a non-runtime constraint: {e:?}"
                    )
                })
                .map_heads(&mut |decl| resolved.wire(decl))
        };
        let generics = args.iter().map(to_runtime).collect();
        let associated_types = assoc
            .iter()
            .map(|(n, t)| (n.clone(), to_runtime(t)))
            .collect();
        Some(bex_vm_types::anchor_interface(&RuntimeInterface::new(
            resolved.wire(&qtn),
            generics,
            associated_types,
        )))
    };
    // A method's runtime signature: Required params -> positional `args`,
    // optional (defaulted) params -> `kwargs`; the `self` receiver is

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Rewrite the interface constraint to use a runtime-representable type
  2. Fix the pre-lowering stage so constraint-only types are filtered before this point
  3. Extend `baml_type::lower_to_runtime` if the constraint should be representable
  4. Convert the `unreachable!` into a typed compiler error for better diagnostics

Example fix

// before
interface Foo<T: SomeConstraintOnlyType> { ... }
// after
interface Foo<T: runtime.Supported> { ... }
Defensive patterns

Strategy: validation

Validate before calling

for arg in &iface.generic_args {
    if lower_to_runtime(arg, &aliases).is_none() {
        return Err(TypeError::NonRuntimeConstraint(iface.name.clone()));
    }
}

Try / catch

std::panic::catch_unwind(|| build_interface_def(resolved, store))
    .unwrap_or_else(|_| report_compiler_bug("interface has non-runtime constraint"));

Prevention

When it happens

Trigger: An interface whose generic argument or bound lowers to a non-runtime type (bare type variable head, function type, or other constraint-only type) reaches the `to_runtime` closure in build_interface_def.

Common situations: Interfaces with bounds referencing types the runtime cannot materialize; new `Ty` variants added to the type system without corresponding `lower_to_runtime` support.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/35b7f0b451cea13c. Report an issue: GitHub.