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 isView on GitHub (pinned to bd85ce9dee)
Solutions
- Rewrite the interface constraint to use a runtime-representable type
- Fix the pre-lowering stage so constraint-only types are filtered before this point
- Extend `baml_type::lower_to_runtime` if the constraint should be representable
- 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
- Filter constraint-only types before runtime lowering in build_interface_def
- Keep lower_to_runtime coverage tests in sync with new Ty variants
- Prefer a typed diagnostic over unreachable! for constraint lowering failures
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
- interface `{iface_wire}` declares a non-runtime type: {e:?}
- interface frame starts with Self
- split_interface matched an interface
- sys_op callee must resolve to a statically-known global func
- expected jump instruction at index {instruction_idx}
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/35b7f0b451cea13c.
Report an issue: GitHub.