BoundaryML/baml · critical
exception table: handler block {handler:?} has no PC address
Error message
exception table: handler block {handler:?} has no PC address — catch region was emitted but its handler block was dropped What it means
Panic (via unreachable!) while building the exception table: a MIR catch region's handler block, after jump-target resolution, has no entry in block_addresses. The catch region was emitted (so the compiler believes a handler is needed) but the handler block itself was dropped during emission. Every emitted catch region must have a handler block with a concrete PC.
Source
Thrown at baml_language/crates/baml_compiler2_emit/src/emit.rs:2830
/// anchor it), and both escaped their `catch` before this was made exact.
///
/// Nested regions overlap: the protected PC set of an inner region is a
/// subset of every enclosing region's (inner windows nest inside outer
/// windows at lowering). The VM picks the innermost covering entry —
/// largest `start_pc`, then smallest `end_pc`, then latest table order —
/// which subset-nesting makes unambiguous: the inner region's coalesced
/// range around any PC is contained in the outer's, and for byte-identical
/// ranges the stable sort below preserves `catch_regions` creation order
/// (always outer before inner), so the last matching entry is the inner
/// handler.
fn build_exception_table(&mut self, mir: &MirFunctionBody<'ctx>) {
use bex_vm_types::bytecode::{ExceptionTableEntry, HandlerContextEntry};
for region in &mir.catch_regions {
let handler = self.analysis.resolve_jump_target(region.handler);
let &handler_pc = self.block_addresses.get(&handler).unwrap_or_else(|| {
unreachable!(
"exception table: handler block {handler:?} has no PC address — \
catch region was emitted but its handler block was dropped"
)
});
// If the error local was optimized away (e.g. an inline
// `throw X catch ...` that the MIR lowers as a direct jump),
// the catch region doesn't need a VM-level exception table entry.
let Some(&error_slot) = self.local_slots.get(®ion.error_local) else {
log::debug!(
"exception table: error local {:?} has no slot (optimized away)",
region.error_local,
);
continue;
};
let stack_trace_slot = region
.stack_trace_local
.and_then(|local| self.local_slots.get(&local).copied())View on GitHub (pinned to bd85ce9dee)
Solutions
- Keep the handler block alive whenever a catch region referencing it is emitted, or drop both together.
- Record a redirect address for eliminated handler blocks so the exception table can point at the surviving equivalent.
- Reproduce with the try/catch BAML source and file a compiler bug including the MIR dump.
- Use a compiler build without the offending block-elimination change.
Defensive patterns
Strategy: fallback
Try / catch
// compiler panic — not catchable in-process; guard the build
if !baml_cli_compile(src).is_ok() {
report_compiler_bug(src);
} Prevention
- Keep catch regions and their handler blocks alive/dropped together
- Assert handler-block coverage before building the exception table
- Add CI tests for try/catch under dead-code elimination
- Pin a known-good compiler version while a regression is open
When it happens
Trigger: Finalizing a function with mir.catch_regions non-empty where the resolved handler block was never emitted, e.g. the handler block was eliminated by an optimization but its catch region survived.
Common situations: Compiler development around try/catch lowering or dead-block elimination in BAML's MIR-to-bytecode pipeline; users see it as a compiler crash on some try/catch programs.
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
- missing block address for jump target {target_block:?}; targ
- missing trap PC for dead-unreachable jump target
- expected jump instruction at index {instruction_idx}
- make_closure: lambda_idx {lambda_idx} out of range
- ntypeargs fits u16
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/8940b042c47203d7.
Report an issue: GitHub.