{"record":{"id":"652c5df66b253a31","repo":"BoundaryML/baml","slug":"engine-lock-poisoned","errorCode":null,"errorMessage":"Engine lock poisoned","messagePattern":"Engine lock poisoned","errorType":"exception","errorClass":"BridgeError","httpStatus":null,"severity":"critical","filePath":"baml_language/crates/bridge_cffi/src/error.rs","lineNumber":16,"sourceCode":"//! Error types for bridge_cffi.\n\nuse thiserror::Error;\n\n/// Errors that can occur during bridge operations.\n#[derive(Debug, Error)]\npub enum BridgeError {\n    #[error(transparent)]\n    Ctypes(#[from] bridge_ctypes::CtypesError),\n    #[error(\"Engine not initialized. Call create_baml_runtime first.\")]\n    NotInitialized,\n\n    #[error(\"Project not initialized\")]\n    ProjectNotInitialized,\n\n    #[error(\"Engine lock poisoned\")]\n    LockPoisoned,\n\n    #[error(\"{0}\")]\n    Runtime(#[from] bex_project::RuntimeError),\n\n    #[error(\"CallFunctionArgs.call_target must be set\")]\n    MissingCallTarget,\n\n    #[error(\"type arguments are not supported when invoking a BAML function handle\")]\n    FunctionHandleTypeArgs,\n\n    #[error(\"Function not found: {name}\")]\n    FunctionNotFound { name: String },\n\n    #[error(\"Missing argument '{parameter}' for function '{function}'\")]\n    MissingArgument { function: String, parameter: String },\n\n    #[error(\"Not implemented: {0}\")]","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/bridge_cffi/src/error.rs#L1-L34","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nRUNTIME_INSTANCE\n    .read()\n    .map_err(|_| BridgeError::LockPoisoned)?\n    .clone()\n    .ok_or(BridgeError::NotInitialized)\n// after\nRUNTIME_INSTANCE\n    .read()\n    .unwrap_or_else(|poisoned| poisoned.into_inner())\n    .clone()\n    .ok_or(BridgeError::NotInitialized)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match get_runtime() {\n    Err(BridgeError::LockPoisoned) => {\n        // a prior thread panicked holding the engine lock; restart/re-init\n        restart_bridge();\n    }\n    other => other?,\n}","preventionTips":["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."],"tags":["concurrency","rust","poisoned-lock","panic"],"backgroundTag":"invalid-state-transition","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}