laravel/framework · error · RuntimeException

Transactions Manager has not been set.

Error message

Transactions Manager has not been set.

What it means

RuntimeException from afterCommit() when the transactions manager ($transactionsManager) is not set on the connection. afterCommit() defers a callback until the surrounding transaction commits; without a manager to track transaction state there is nothing to defer onto, so the call is rejected rather than silently dropped.

Solutions

  1. Guard the call: only register an afterCommit callback when inside a transaction, e.g. if (DB::connection()->transactionLevel() > 0) { ... } else { run it immediately }.
  2. Wrap the work in DB::transaction() so a transactions manager is established before afterCommit is called.
  3. If the callback should run regardless, skip afterCommit and run the logic directly (or dispatch a job).
  4. Ensure the connection is fully bootstrapped via the standard Laravel DB facade/service-provider wiring.

Example fix

// before
// in an observer, called from a queue worker with no DB transaction
DB::connection()->afterCommit(fn () => Cache::forget("user:{$user->id}"));
// after
if (DB::connection()->transactionLevel() > 0) {
    DB::connection()->afterCommit(fn () => Cache::forget("user:{$user->id}"));
} else {
    Cache::forget("user:{$user->id}");
}
Defensive patterns

Strategy: validation

Validate before calling

// Only register afterCommit callbacks when a transaction (and its manager) is active.
$conn = DB::connection();
if ($conn->transactionLevel() > 0) {
    $conn->afterCommit($callback);
} else {
    // no transaction: run immediately, or dispatch a job
    $callback();
}

Type guard

function afterCommitIsSafe(\Illuminate\Database\ConnectionInterface $conn): bool
{
    // The transactions manager is set whenever a transaction has been started.
    return $conn instanceof \Illuminate\Database\Connection && $conn->transactionLevel() > 0;
}

Try / catch

try {
    DB::connection()->afterCommit($callback);
} catch (\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'Transactions Manager has not been set')) {
        // either open a transaction first, or run the callback directly
        $callback();
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Calling DB::connection()->afterCommit(...) outside of any transaction context; calling afterCommit on a connection before a transaction manager is attached (e.g. in low-level/CLI bootstrapping or with a custom connection); using a connection type that does not initialize the transactions manager.

Common situations: Model observers/listeners calling afterCommit when invoked from a queue worker or CLI command with no active DB transaction; testing with a connection that bypasses normal Laravel bootstrapping; using afterCommit where you actually mean after the immediate save regardless of transactions.

Related errors


AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11). Data as JSON: /api/errors/c24ecb7682619974. Report an issue: GitHub.

Appendix: source

Thrown at src/Illuminate/Database/Concerns/ManagesTransactions.php:360

    {
        return $this->transactions;
    }

    /**
     * Execute the callback after a transaction commits.
     *
     * @param  callable  $callback
     * @return void
     *
     * @throws \RuntimeException
     */
    public function afterCommit($callback)
    {
        if ($this->transactionsManager) {
            return $this->transactionsManager->addCallback($callback);
        }

        throw new RuntimeException('Transactions Manager has not been set.');
    }

    /**
     * Execute the callback after a transaction rolls back.
     *
     * @param  callable  $callback
     * @return void
     *
     * @throws \RuntimeException
     */
    public function afterRollBack($callback)
    {
        if ($this->transactionsManager) {
            return $this->transactionsManager->addCallbackForRollback($callback);
        }

        throw new RuntimeException('Transactions Manager has not been set.');
    }

View on GitHub (pinned to e0f6eb3518)