laravel/framework · error · RuntimeException

Unable to bind custom driver callback

Error message

Unable to bind custom driver callback

What it means

Thrown by AuthManager::extend() when bindCallbackToSelf() returns null. bindCallbackToSelf calls Closure::bindTo() on anonymous closures; bindTo returns null if PHP cannot rebind the closure to the requested scope, so the manager refuses to store an unusable creator.

Source

Thrown at src/Illuminate/Auth/AuthManager.php:280

        $this->userResolver = $userResolver;

        return $this;
    }

    /**
     * Register a custom driver creator Closure.
     *
     * @param  string  $driver
     * @param  \Closure  $callback
     *
     * @param-closure-this  $this  $callback
     *
     * @return $this
     */
    public function extend($driver, Closure $callback)
    {
        try {
            $callback = $this->bindCallbackToSelf($callback) ?? throw new RuntimeException('Unable to bind custom driver callback');
        } catch (ReflectionException $e) {
            throw new RuntimeException('Unable to bind custom driver callback', previous: $e);
        }

        $this->customCreators[$driver] = $callback;

        return $this;
    }

    /**
     * Register a custom provider creator Closure.
     *
     * @param  string  $name
     * @param  \Closure  $callback
     * @return $this
     */
    public function provider($name, Closure $callback)
    {

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Pass a regular (non-static) Closure so it can be rebound to the manager.
  2. Avoid wrapping the callback with Closure::fromCallable on a method that carries incompatible scope.
  3. If you need a static closure, build the guard instance explicitly and return it instead of relying on rebinding.

Example fix

// before
Auth::extend('custom', static function ($app, $name, $config) {
    // static anonymous closure - bindTo may yield null
    return new CustomGuard(...);
});

// after
Auth::extend('custom', function ($app, $name, $config) {
    return new CustomGuard($app, $name, $config);
});
Defensive patterns

Strategy: validation

Validate before calling

$callback = function ($app, $name, $config) { /* ... */ };
$rebound = $callback->bindTo(app('auth.manager'), app('auth.manager')::class);
if ($rebound === null) {
    throw new RuntimeException('Closure cannot be rebound; use a non-static closure.');
}
Auth::extend('custom', $callback);

Type guard

function isRebindableClosure(\Closure $c, object $scope): bool
{
    try {
        return (new \ReflectionFunction($c))->isAnonymous()
            ? $c->bindTo($scope, $scope::class) !== null
            : true;
    } catch (\ReflectionException) {
        return false;
    }
}

Try / catch

try {
    Auth::extend('custom', $callback);
} catch (\RuntimeException $e) {
    // wrap with an invokable class instead
    Auth::extend('custom', app(CustomGuardFactory::class));
}

Prevention

When it happens

Trigger: Calling Auth::extend('driver', $callback) where $callback is an anonymous closure that cannot be rebound to the AuthManager scope (e.g., a static closure whose bindTo with a scope fails, or a closure over incompatible bound state).

Common situations: Passing a static closure or a closure built from a non-object-bound context in a way that defeats rebinding; very rare in normal usage.

Related errors


AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06). Data as JSON: /data/errors/37124dd4a1cc8c39.json. Report an issue: GitHub.