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() on the null-coalesce-throw line when bindCallbackToSelf($callback) returns null. extend() attempts to rebind the supplied Closure so that $this inside it refers to the AuthManager; if the rebinder cannot produce a bound closure (returns null), it throws this RuntimeException. This is distinct from the ReflectionException branch on the next line.
Solutions
- Pass a non-static closure so it can be rebound to the AuthManager instance.
- If the closure must be static, restructure to avoid relying on $this binding, or wrap logic in an invokable class.
- Ensure the closure is a real Closure (not a first-class callable string in a context that strips scope).
- Confirm PHP version compatibility for Closure::bind / Closure::bindTo behavior.
Example fix
// before — static closure cannot be rebound
Auth::extend('jwt', static function ($app, array $config) {
return new JwtGuard(...);
});
// after — non-static closure
Auth::extend('jwt', function ($app, array $config) {
return new JwtGuard(Auth::createUserProvider($config['provider'] ?? null));
}); Defensive patterns
Strategy: validation
Validate before calling
// PHP — ensure the closure is non-static and rebindable before extend()
$ref = new \ReflectionFunction($callback);
if ($ref->isStatic()) {
throw new \RuntimeException('Cannot extend() with a static closure; use a non-static closure.');
}
Auth::extend($driver, $callback); Type guard
function isRebindableClosure(\Closure $c): bool {
return ! (new \ReflectionFunction($c))->isStatic();
} Try / catch
try {
Auth::extend($driver, $callback);
} catch (\RuntimeException $e) {
Log::error('Auth::extend failed to bind', ['driver' => $driver, 'err' => $e->getMessage()]);
throw $e;
} Prevention
- Avoid static closures when calling Auth::extend().
- Use invokable classes for complex custom drivers.
- Register extend() in a service provider's boot() method.
When it happens
Trigger: Calling Auth::extend($driver, $callback) with a closure that bindCallbackToSelf refuses or cannot rebind (returns null), e.g. a static closure (Closure::bind returns null for static closures) or a Closure from an incompatible scope that cannot be rebound to the manager instance.
Common situations: Registering a custom auth driver with a static closure (static fn (...) => ...); passing a closure built from ReflectionClosure whose declared scope cannot be reassigned; edge cases after upgrading PHP where Closure::bind semantics changed for static closures.
Related errors
- Unable to bind custom driver callback
- Auth driver [ ] for guard [ ] is not defined.
- Auth guard [ ] is not defined.
- Authentication user provider
- Cookie jar has not been set.
AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11).
Data as JSON: /api/errors/37124dd4a1cc8c39.
Report an issue: GitHub.
Appendix: 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 e0f6eb3518)