{"record":{"id":"c549c4fc2d3700f8","repo":"SeaQL/sea-orm","slug":"fail-to-acquire-mocker-c549c4","errorCode":null,"errorMessage":"Fail to acquire mocker","messagePattern":"Fail to acquire mocker","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sea-orm-sync/src/driver/mock.rs","lineNumber":141,"sourceCode":"            execute_counter: AtomicUsize::new(0),\n            query_counter: AtomicUsize::new(0),\n            mocker: Mutex::new(Box::new(m)),\n        }\n    }\n\n    pub(crate) fn get_mocker_mutex(&self) -> &Mutex<Box<dyn MockDatabaseTrait>> {\n        &self.mocker\n    }\n\n    /// Get the [DatabaseBackend](crate::DatabaseBackend) being used by the [MockDatabase]\n    ///\n    /// # Panics\n    ///\n    /// Will panic if the lock cannot be acquired.\n    pub fn get_database_backend(&self) -> DbBackend {\n        self.mocker\n            .lock()\n            .expect(\"Fail to acquire mocker\")\n            .get_database_backend()\n    }\n\n    /// Execute the SQL statement in the [MockDatabase]\n    #[instrument(level = \"trace\", skip(statement))]\n    pub fn execute(&self, statement: Statement) -> Result<ExecResult, DbErr> {\n        debug_print!(\"{}\", statement);\n        let counter = self.execute_counter.fetch_add(1, Ordering::SeqCst);\n        self.mocker\n            .lock()\n            .map_err(exec_err)?\n            .execute(counter, statement)\n    }\n\n    /// Return one [QueryResult] if the query was successful\n    #[instrument(level = \"trace\", skip(statement))]\n    pub fn query_one(&self, statement: Statement) -> Result<Option<QueryResult>, DbErr> {\n        debug_print!(\"{}\", statement);","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/SeaQL/sea-orm/blob/e29bcd1b417c41a553b386fe94511d7c64a1c8ec/sea-orm-sync/src/driver/mock.rs#L123-L159","documentation":"`MockDatabaseConnection::get_database_backend` locks the internal `Mutex<Box<dyn MockDatabaseTrait>>` and panics with \"Fail to acquire mocker\" if the lock is poisoned — i.e. a previous thread panicked while holding the mocker lock. Note that sibling methods like `execute`/`query_all` use `.map_err(...)` and return `DbErr`, but this one (plus `begin`/`commit`/`rollback`) uses `expect`, so a poisoned lock aborts the process instead of returning an error.","triggerScenarios":"Calling `get_database_backend()` (directly or via `DatabaseBackend`/connection helpers) on a `DatabaseConnection` backed by a `MockDatabase` whose mocker mutex was poisoned by an earlier panic in another method (e.g. a user-panic inside `MockDatabase::exec`/`query` callbacks or assert code), or reentrant locking from the same thread.","commonSituations":"Almost always seen in tests: a mock expectation closure panics (assertion failure) while holding the lock, poisoning it; subsequent calls to `get_database_backend` panic with this message. Also appears when the mock connection is shared across threads in sync code and a worker panics mid-query.","solutions":["Fix the original panic that poisoned the lock — look for the first panic in the test output (often a failing mock assertion).","Use `MockDatabaseConnection::get_mocker_mutex()` with `lock().unwrap_or_else(|p| p.into_inner())` if you need lock-free-after-poison access in your own code.","Avoid panicking inside mock callback/expectation code; return `Err` values from `MockDatabase` trait methods instead.","Patch the call site defensively: replace `.expect(...)` with `.map_err(...)`/recovery via `PoisonError::into_inner`, matching the `execute`/`query_all` pattern.","Isolate each mock database per test so one panicking test cannot poison a shared mocker."],"exampleFix":"// before\npub fn get_database_backend(&self) -> DbBackend {\n    self.mocker.lock().expect(\"Fail to acquire mocker\").get_database_backend()\n}\n// after — tolerate a poisoned lock instead of panicking\npub fn get_database_backend(&self) -> DbBackend {\n    self.mocker\n        .lock()\n        .unwrap_or_else(|poisoned| poisoned.into_inner())\n        .get_database_backend()\n}","handlingStrategy":"type-guard","validationCode":"// Check the mock connection is usable (lock not poisoned) before queries\nfn mock_backend_available(conn: &MockDatabaseConnection) -> bool {\n    !conn.get_mocker_mutex().is_poisoned()\n}","typeGuard":"fn mocker_lock(conn: &MockDatabaseConnection) -> Option<MutexGuard<'_, Box<dyn MockDatabaseTrait>>> {\n    conn.get_mocker_mutex().lock().ok()\n}","tryCatchPattern":"let backend = std::panic::catch_unwind(AssertUnwindSafe(|| conn.get_database_backend()))\n    .unwrap_or_else(|_| {\n        eprintln!(\"mock mocker lock poisoned; recreating MockDatabase\");\n        rebuild_mock_connection()\n    });","preventionTips":["Fix the root panic that poisoned the lock — later panics are secondary symptoms","Create a fresh MockDatabase per test so poisoning cannot leak between tests","Never panic inside mock expectation/callback code; return DbErr instead","Avoid calling back into the connection from within MockDatabaseTrait methods (reentrant lock)","Use is_poisoned() checks or unwrap_or_else(PoisonError::into_inner) in custom helper code"],"tags":["panic","mutex-poisoned","mock","testing","concurrency"],"backgroundTag":"internal-invariant-violation","analyzedSha":"e29bcd1b417c41a553b386fe94511d7c64a1c8ec","analyzedAt":"2026-09-10T11:31:52.468Z","contentChangedAt":"2026-09-10T11:31:52.468Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}