{"record":{"id":"a13da6aa8c5bdae0","repo":"t8y2/dbx","slug":"driver-operation-lock-table-poisoned","errorCode":null,"errorMessage":"driver operation lock table poisoned","messagePattern":"driver operation lock table poisoned","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbx-core/src/agent_service.rs","lineNumber":893,"sourceCode":"\n    progress(AgentProgressEvent::step(\"all-done\"));\n    Ok(result)\n}\n\nfn is_cancelled_error(error: &str) -> bool {\n    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    })","sourceCodeStart":875,"sourceCodeEnd":911,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/crates/dbx-core/src/agent_service.rs#L875-L911","documentation":"driver_operation_lock acquires the process-wide driver_operation_locks table guarded by a std::sync::Mutex. .lock() returns Err (poisoned) if another thread panicked while holding the mutex; the expect(\"driver operation lock table poisoned\") then panics in turn. It signals that some earlier driver-operation code path panicked while mutating the lock table.","triggerScenarios":"Any driver operation (install_agent_driver_with_batch, uninstall_agent_driver, import_agent_driver, ensure_agent_driver_ready_from, install_agent_driver_from_registry_locked) panics while holding the driver_operation_locks mutex; the next caller of driver_operation_lock then hits the poisoned lock and panics.","commonSituations":"An earlier install/uninstall panicked (bug, assertion, failed expect like 1082) and left the mutex poisoned; subsequent operations that should work now cascade-fail; in tests this shows as an unexpected panic on the second operation after a deliberately panicking case.","solutions":["Find and fix the original panic that poisoned the mutex — the poisoned-guard payload usually contains it.","If resilience is needed, use lock().unwrap_or_else(|p| p.into_inner()) to recover, since the table is just an entry map.","Prefer scoped locking that avoids panicking while the guard is held (no expect/unwrap inside the critical section).","Consider a parking_lot::Mutex (non-poisoning) for lock tables like this."],"exampleFix":"// before\nlet mut locks = am.driver_operation_locks.lock().expect(\"driver operation lock table poisoned\");\n// after\nlet mut locks = am.driver_operation_locks.lock().unwrap_or_else(|poisoned| poisoned.into_inner());","handlingStrategy":"try-catch","validationCode":"// detect poisoning early in health checks\nif am.driver_operation_locks.is_poisoned() {\n    tracing::error!(\"driver_operation_locks mutex is poisoned; restart or recover\");\n}","typeGuard":"fn lock_table_healthy(locks: &std::sync::Mutex<DriverLockTable>) -> bool {\n    !locks.is_poisoned()\n}","tryCatchPattern":"let mut locks = am.driver_operation_locks.lock().unwrap_or_else(|poisoned| {\n    tracing::warn!(\"driver lock table was poisoned; recovering: {:?}\", poisoned);\n    poisoned.into_inner()\n});","preventionTips":["Never unwrap/expect/panic while holding a std Mutex guard.","Return Result from code that mutates the lock table.","Consider parking_lot::Mutex for non-poisoning semantics.","Log the original panic payload from PoisonError to find the root cause fast."],"tags":["rust","mutex-poisoning","panic","concurrency","locking"],"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"}