{"record":{"id":"824d3b7f52ec9b38","repo":"t8y2/dbx","slug":"jre-install-lock-table-poisoned","errorCode":null,"errorMessage":"JRE install lock table poisoned","messagePattern":"JRE install lock table poisoned","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbx-core/src/agent_service.rs","lineNumber":899,"sourceCode":"    error.contains(AGENT_DOWNLOAD_CANCELED_ERROR)\n}\n\nasync fn can_fallback_to_local_agent(\n    _am: &AgentManager,\n    _db_type: &str,\n    cancellations: &[&AgentInstallCancellation],\n) -> bool {\n    !cancellations.iter().any(|token| token.is_cancelled())\n}\n\nfn driver_operation_lock<'a>(am: &'a AgentManager, db_type: &str) -> OperationLockHandle<'a> {\n    let mut locks = am.driver_operation_locks.lock().expect(\"driver operation lock table poisoned\");\n    let lock = locks.entry(db_type.to_string()).or_insert_with(|| Arc::new(tokio::sync::Mutex::new(()))).clone();\n    OperationLockHandle::new(&am.driver_operation_locks, db_type, lock)\n}\n\nfn jre_operation_lock<'a>(am: &'a AgentManager, jre_key: &str) -> OperationLockHandle<'a> {\n    let mut locks = am.jre_install_locks.lock().expect(\"JRE install lock table poisoned\");\n    let lock = locks.entry(jre_key.to_string()).or_insert_with(|| Arc::new(tokio::sync::Mutex::new(()))).clone();\n    OperationLockHandle::new(&am.jre_install_locks, jre_key, lock)\n}\n\n/// Future that resolves as soon as any cancellation token fires.\nfn first_cancellation<'a>(\n    cancellations: &'a [&'a AgentInstallCancellation],\n) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 'a>> {\n    Box::pin(async move {\n        let ((), _index, _rest) =\n            futures::future::select_all(cancellations.iter().map(|token| Box::pin(token.cancelled()))).await;\n    })\n}\n\n/// Acquire a per-driver/JRE operation lock, aborting promptly if any\n/// cancellation token fires while the lock is held elsewhere. Without this\n/// race a cancelled install would wait for the current lock holder to finish\n/// before it could observe its token.","sourceCodeStart":881,"sourceCodeEnd":917,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/crates/dbx-core/src/agent_service.rs#L881-L917","documentation":"Same family as the driver lock table: jre_operation_lock locks the jre_install_locks mutex table and panics with \"JRE install lock table poisoned\" if the mutex is poisoned, i.e. a previous thread panicked while holding it. Called by uninstall_agent_jre, reinstall_agent_jre_from, ensure_jre_from_registry and JRE lock tests.","triggerScenarios":"A JRE install/uninstall/reinstall path panics while holding the jre_install_locks mutex guard; the next call to jre_operation_lock (for any jre_key) observes the poisoned mutex and panics.","commonSituations":"A prior JRE operation aborted with a panic (e.g. failed expect or assertion in extraction logic); in tests, a case that intentionally panics poisons the shared manager state for later assertions; a long-running app where one corrupted JRE package causes cascading failures on subsequent JRE operations.","solutions":["Fix the root-cause panic that poisoned jre_install_locks first.","Recover with lock().unwrap_or_else(|p| p.into_inner()) if the table can be safely reused after a panic.","Avoid panicking inside the mutex critical section; return Results from mutation code.","Switch the table to parking_lot::Mutex to eliminate poisoning entirely."],"exampleFix":"// before\nlet mut locks = am.jre_install_locks.lock().expect(\"JRE install lock table poisoned\");\n// after\nlet mut locks = am.jre_install_locks.lock().unwrap_or_else(|poisoned| poisoned.into_inner());","handlingStrategy":"try-catch","validationCode":"// health check for JRE lock table\nif am.jre_install_locks.is_poisoned() {\n    tracing::error!(\"jre_install_locks mutex is poisoned\");\n}","typeGuard":"fn jre_locks_healthy(locks: &std::sync::Mutex<JreLockTable>) -> bool {\n    !locks.is_poisoned()\n}","tryCatchPattern":"let mut locks = am.jre_install_locks.lock().unwrap_or_else(|poisoned| {\n    tracing::warn!(\"JRE lock table poisoned; recovering: {:?}\", poisoned);\n    poisoned.into_inner()\n});","preventionTips":["Fix panics in JRE install/uninstall paths before they poison shared state.","Keep mutex critical sections free of panicking calls.","Use parking_lot::Mutex to remove poisoning as a failure mode.","In tests, run panicking JRE cases in isolated processes or fresh manager instances."],"tags":["rust","mutex-poisoning","panic","concurrency","jre-install"],"backgroundTag":"mutex-poisoned","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}