BoundaryML/baml · critical · BridgeError
Engine lock poisoned
Error message
Engine lock poisoned
What it means
BridgeError::LockPoisoned is returned when the global RUNTIME_INSTANCE RwLock (read or write) could not be acquired because another thread panicked while holding the lock, leaving it poisoned (see get_runtime/replace_runtime/take_runtime in lib_native.rs). Once poisoned, every subsequent bridge operation touching the engine slot fails with this error. It signals a prior panic inside engine-protected code rather than a problem with the current call.
Source
Thrown at baml_language/crates/bridge_cffi/src/error.rs:16
//! Error types for bridge_cffi.
use thiserror::Error;
/// Errors that can occur during bridge operations.
#[derive(Debug, Error)]
pub enum BridgeError {
#[error(transparent)]
Ctypes(#[from] bridge_ctypes::CtypesError),
#[error("Engine not initialized. Call create_baml_runtime first.")]
NotInitialized,
#[error("Project not initialized")]
ProjectNotInitialized,
#[error("Engine lock poisoned")]
LockPoisoned,
#[error("{0}")]
Runtime(#[from] bex_project::RuntimeError),
#[error("CallFunctionArgs.call_target must be set")]
MissingCallTarget,
#[error("type arguments are not supported when invoking a BAML function handle")]
FunctionHandleTypeArgs,
#[error("Function not found: {name}")]
FunctionNotFound { name: String },
#[error("Missing argument '{parameter}' for function '{function}'")]
MissingArgument { function: String, parameter: String },
#[error("Not implemented: {0}")]View on GitHub (pinned to bd85ce9dee)
Solutions
- Find and fix the root-cause panic that poisoned the lock (check logs/stderr for the original panic backtrace).
- Restart the process or re-initialize the bridge module — poisoning is per-lock and does not self-heal.
- In the bridge, switch to parking_lot::RwLock (no poisoning) or recover from poison via lock.unwrap_or_else(|e| e.into_inner()) if the state is still valid.
- Avoid panics inside engine callbacks by converting failures to BridgeError results instead of unwrapping.
Example fix
// before
RUNTIME_INSTANCE
.read()
.map_err(|_| BridgeError::LockPoisoned)?
.clone()
.ok_or(BridgeError::NotInitialized)
// after
RUNTIME_INSTANCE
.read()
.unwrap_or_else(|poisoned| poisoned.into_inner())
.clone()
.ok_or(BridgeError::NotInitialized) Defensive patterns
Strategy: try-catch
Try / catch
match get_runtime() {
Err(BridgeError::LockPoisoned) => {
// a prior thread panicked holding the engine lock; restart/re-init
restart_bridge();
}
other => other?,
} Prevention
- Never panic while holding engine locks; return BridgeError results instead of unwrapping.
- Capture and fix the original panic backtrace — LockPoisoned is always downstream of it.
- Use parking_lot::RwLock or into_inner() recovery if the engine state remains valid after a panic.
- Monitor for LockPoisoned occurrences as a critical health signal and restart the affected worker.
When it happens
Trigger: A panic occurred in any thread while holding the RUNTIME_INSTANCE read/write guard (e.g. inside an engine callback, shutdown, or install path); afterwards, get_runtime, replace_runtime, or take_runtime call .lock()/.read()/.write() and map the Err poison result to BridgeError::LockPoisoned.
Common situations: A panicked async task or callback earlier in the run corrupted the lock; concurrent hot-reload/shutdown racing with in-flight calls; tests that let one setup thread panic and then observe all later calls fail; bug in an embedded host calling the bridge reentrantly from a panic hook.
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
- sys_op callee must resolve to a statically-known global func
- expected jump instruction at index {instruction_idx}
- exhaustive realized-leaf template classification
- ntypeargs fits u16
- interface `{iface_wire}` declares a non-runtime type: {e:?}
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/652c5df66b253a31.
Report an issue: GitHub.