BoundaryML/baml · error · VmError

uncaught throw: {value:?}

Error message

uncaught throw: {value:?}

What it means

VmError::ThrownUnhandled represents an exception that escaped all catch handlers in the BAML VM, carrying the thrown Value plus a captured stack trace (Vec<StackFrame>). Unlike the internal Thrown signal, this is the final, user-visible form: the throw propagated past every handler and terminated execution.

Source

Thrown at baml_language/crates/bex_vm_types/src/errors.rs:437

            origin: VmUnwindOrigin::unresolved(source),
        }
    }

    #[must_use]
    pub const fn with_origin(mut self, origin: VmUnwindOrigin) -> Self {
        self.origin = origin;
        self
    }
}

/// Any kind of virtual machine error.
#[derive(Debug, Error, PartialEq, Clone)]
pub enum VmError {
    /// Catchable (panics and error values) — internal signal for exception unwinding.
    #[error("uncaught throw: {0:?}")]
    Thrown(VmThrown),
    /// An exception that escaped all catch handlers, with captured stack trace.
    #[error("uncaught throw: {value:?}")]
    ThrownUnhandled {
        value: Value,
        trace: Vec<StackFrame>,
    },
    /// Fatal VM errors
    #[error("{0}")]
    InternalError(#[from] VmInternalError),
    /// Fatal VM error with captured stack trace.
    /// Produced by `exec()` wrapper from `InternalError`.
    #[error("{}", format_internal_error(source, trace))]
    TracedInternalError {
        source: VmInternalError,
        trace: Vec<StackFrame>,
    },
}

impl VmError {
    #[must_use]

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Read the attached stack trace to locate the innermost frame that threw.
  2. Add a catch handler at the appropriate level for the thrown value type.
  3. Handle the value at the host-language boundary if BAML-level handling is not desired.
  4. Fix the root cause in the throwing frame (invalid input, failed client call, etc.).

Example fix

// before: unhandled deep throw terminates the run
fn main() {
  Deep.helper()
}

// after: handle at the call site
fn main() {
  let r = catch Deep.helper() {
    case e => Log("failed: ", e); null
  }
}
Defensive patterns

Strategy: try-catch

Try / catch

match vm_result {
    Err(BexError::ThrownUnhandled { value, trace }) => {
        eprintln!("unhandled throw {:?} at:", value);
        for frame in &trace { eprintln!("  {}", frame); }
    }
    other => other?,
}

Prevention

When it happens

Trigger: A thrown error value traverses the entire call stack without any catch handler matching; the top-level BAML function itself throws; a panic raised in native code is not converted/handled by any frame.

Common situations: Running a BAML program where a deep helper throws (bad JSON parse, failed LLM call surfaced as a throw) and only the entry point sees the failure; missing catch blocks added when new fallible code paths were introduced.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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