SeaQL/sea-orm · error

Fail to acquire mocker

Error message

Fail to acquire mocker

What it means

The MockDatabase connector calls .lock().expect("Fail to acquire mocker") on a Mutex guarding the mocker. If another thread panicked while holding the lock, the mutex is poisoned and lock() returns Err, causing this panic. It indicates a poisoned-lock situation, not a logic error in your test code itself.

Source

Thrown at src/driver/mock.rs:142

            execute_counter: AtomicUsize::new(0),
            query_counter: AtomicUsize::new(0),
            mocker: Mutex::new(Box::new(m)),
        }
    }

    pub(crate) fn get_mocker_mutex(&self) -> &Mutex<Box<dyn MockDatabaseTrait>> {
        &self.mocker
    }

    /// Get the [DatabaseBackend](crate::DatabaseBackend) being used by the [MockDatabase]
    ///
    /// # Panics
    ///
    /// Will panic if the lock cannot be acquired.
    pub fn get_database_backend(&self) -> DbBackend {
        self.mocker
            .lock()
            .expect("Fail to acquire mocker")
            .get_database_backend()
    }

    /// Execute the SQL statement in the [MockDatabase]
    #[instrument(level = "trace", skip(statement))]
    pub fn execute(&self, statement: Statement) -> Result<ExecResult, DbErr> {
        debug_print!("{}", statement);
        let counter = self.execute_counter.fetch_add(1, Ordering::SeqCst);
        self.mocker
            .lock()
            .map_err(exec_err)?
            .execute(counter, statement)
    }

    /// Return one [QueryResult] if the query was successful
    #[instrument(level = "trace", skip(statement))]
    pub fn query_one(&self, statement: Statement) -> Result<Option<QueryResult>, DbErr> {
        debug_print!("{}", statement);

View on GitHub (pinned to e29bcd1b41)

Solutions

  1. Fix the original panic that poisoned the lock — inspect the panic message from the first failing thread/test.
  2. Give each test its own MockDatabase/connector instead of sharing one across threads.
  3. Use lock().unwrap_or_else(|p| p.into_inner()) if you prefer to recover from poisoning rather than panic.
  4. Avoid panicking assertions inside mock callbacks; return errors instead.

Example fix

// before
self.mocker.lock().expect("Fail to acquire mocker").get_database_backend()
// after
self.mocker.lock().unwrap_or_else(|poisoned| poisoned.into_inner()).get_database_backend()
Defensive patterns

Strategy: validation

Validate before calling

// Before using the shared mock connector, check the lock is not poisoned:
assert!(!connector.mocker.is_poisoned(), "mocker lock poisoned by an earlier panic");

Type guard

fn mocker_healthy(c: &MockDatabaseConnector) -> bool { !c.mocker.is_poisoned() }

Try / catch

let backend = std::panic::catch_unwind(AssertUnwindSafe(|| connector.get_database_backend()))
    .unwrap_or_else(|_| DbBackend::Postgres); // fallback for poisoned lock

Prevention

When it happens

Trigger: Calling MockDatabaseConnector::get_database_backend() after any thread panicked while holding the mocker mutex (e.g. a panicked assert inside a mock callback).

Common situations: Parallel tests sharing one MockDatabase connector where one test panics mid-mock; a mock closure that unwraps on unexpected SQL, poisoning the lock for all subsequent calls.

Related errors


AI-assisted analysis of SeaQL/sea-orm@e29bcd1b41 (2026-09-10). Data as JSON: /api/errors/e620bbbab54f8417. Report an issue: GitHub.