SeaQL/sea-orm · error

Failed to acquire mocker

Error message

Failed to acquire mocker

What it means

`MockDatabaseConnection::begin` locks the mocker mutex and panics with "Failed to acquire mocker" if the lock is poisoned (a prior panic occurred while another thread held it). Transaction begin on the mock backend cannot return an error, so the library escalates lock poisoning to a hard panic. Functionally identical to error 67 but on the transaction-begin path.

Source

Thrown at sea-orm-sync/src/driver/mock.rs:208

        #[cfg(feature = "sync")]
        {
            match self.query_all(statement.clone()) {
                Ok(v) => Box::new(v.into_iter().map(Ok)),
                Err(e) => Box::new(Some(Err(e)).into_iter()),
            }
        }
    }

    /// Create a statement block  of SQL statements that execute together.
    ///
    /// # Panics
    ///
    /// Will panic if the lock cannot be acquired.
    #[instrument(level = "trace")]
    pub fn begin(&self) {
        self.mocker
            .lock()
            .expect("Failed to acquire mocker")
            .begin()
    }

    /// Commit a transaction atomically to the database
    ///
    /// # Panics
    ///
    /// Will panic if the lock cannot be acquired.
    #[instrument(level = "trace")]
    pub fn commit(&self) {
        self.mocker
            .lock()
            .expect("Failed to acquire mocker")
            .commit()
    }

    /// Roll back a faulty transaction
    ///

View on GitHub (pinned to e29bcd1b41)

Solutions

  1. Find and fix the first panic in the run — it poisoned the mocker lock; later "Failed to acquire mocker" panics are secondary.
  2. Don't call back into the connection (begin/commit/execute) from inside `MockDatabaseTrait` callbacks to avoid reentrant locking.
  3. Give each test its own `MockDatabase`/connection instead of sharing one across tests or threads.
  4. Recover defensively with `lock().unwrap_or_else(|p| p.into_inner())` if a poisoned-but-usable mocker is acceptable in your harness.
  5. Return errors from mock logic rather than panicking, so the lock is released cleanly.

Example fix

// before
pub fn begin(&self) {
    self.mocker.lock().expect("Failed to acquire mocker").begin()
}
// after
pub fn begin(&self) {
    self.mocker
        .lock()
        .unwrap_or_else(|poisoned| poisoned.into_inner())
        .begin()
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Only begin a mock transaction when the mocker lock is healthy
if conn.get_mocker_mutex().is_poisoned() {
    eprintln!("mock connection poisoned; recreating before begin()");
    conn = recreate_mock_connection();
}

Type guard

fn can_begin_mock_txn(conn: &MockDatabaseConnection) -> bool {
    !conn.get_mocker_mutex().is_poisoned()
}

Try / catch

std::panic::catch_unwind(AssertUnwindSafe(|| txn.begin()))
    .unwrap_or_else(|_| {
        eprintln!("begin() panicked on poisoned mocker; building a new mock db");
        // fall back to a fresh DatabaseConnection over a new MockDatabase
    });

Prevention

When it happens

Trigger: Beginning a transaction (`Database::begin` / `db.begin()`) against a mock-backed connection whose `Mutex<Box<dyn MockDatabaseTrait>>` was poisoned by an earlier panic, or attempting reentrant lock acquisition from code already holding the mocker lock (e.g. inside a mock callback that calls `begin`).

Common situations: Typical in test suites: a panicking mock expectation or an earlier test failure poisons the shared mock connection; the next `db.begin()` panics with this message instead of failing the test cleanly. Also seen with mock connections shared across async tasks run on multiple threads.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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