laravel/framework · error · RuntimeException
Unable to bind custom driver callback
Error message
Unable to bind custom driver callback
What it means
CacheManager::extend uses the RebindsCallbacksToSelf trait to rebind the registered Closure to the CacheManager instance. bindCallbackToSelf returns the rebound closure, or null if Closure::bindTo() failed (e.g., binding a static closure to an incompatible scope, or binding a Closure backed by an internal/ReflectionFunction that cannot be rebound). When the result is null, the ?? throw fires immediately.
Solutions
- Pass a non-static anonymous Closure to Cache::extend: Cache::extend('mydriver', function ($app, $config) { ... }) without the 'static' prefix.
- If using Closure::fromCallable, wrap it: Cache::extend('mydriver', fn (...$args) => $callable(...$args)).
- Avoid binding closures from internal/extension classes; re-author as PHP userland closures.
- Inspect the chained exception (null in this branch) — note this branch has no previous, so the cause is the null return from bindTo; switch to error 31's pattern for reflection failures.
Example fix
// before
Cache::extend('mydriver', \Closure::fromCallable([MyDriverFactory::class, 'make']));
// after
Cache::extend('mydriver', function ($app, array $config) {
return Cache::repository(new MyDriverStore($config));
}); Defensive patterns
Strategy: validation
Validate before calling
// Validate the closure is a non-static anonymous Closure before extending
$callback = function ($app, array $config) {
return Cache::repository(new MyDriverStore($config));
};
$reflector = new \ReflectionFunction($callback);
if (! $reflector->isAnonymous() || $reflector->isStatic()) {
throw new \LogicException('Cache::extend expects a non-static anonymous Closure.');
}
Cache::extend('mydriver', $callback); Type guard
function isBindableAnonymousClosure(\Closure $closure): bool
{
$r = new \ReflectionFunction($closure);
return $r->isAnonymous() && ! $r->isStatic();
} Try / catch
try {
Cache::extend('mydriver', $callback);
} catch (\RuntimeException $e) {
if ($e->getMessage() === 'Unable to bind custom driver callback') {
// re-author as a plain anonymous closure and retry
Cache::extend('mydriver', function ($app, array $config) use ($callback) {
return $callback($app, $config);
});
} else {
throw $e;
}
} Prevention
- Always pass plain anonymous Closures (function () {}) to Cache::extend, never static fn or Closure::fromCallable on static methods.
- Wrap third-party callbacks before passing to extend: function (...$a) use ($cb) { return $cb(...$a); }.
- Add a project lint: grep for Cache::extend with 'static fn' and reject in CI.
When it happens
Trigger: Calling Cache::extend('mydriver', Closure) where the Closure is a static fn () => ... that cannot be rebound to the CacheManager scope, or a Closure created from a method reference (\Closure::fromCallable on a static method) that bindTo returns null for. Also from Closures backed by internal classes.
Common situations: Passing Closure::fromCallable([SomeClass::class, 'staticMethod']) to Cache::extend(). Using a static closure assuming it would behave like a normal one. Providing a closure from an opcache-compiled file with a corrupted scope table.
Related errors
- Cache store [ ] is not defined.
- Cache value for key [ ] must be a boolean, given.
- Cache value for key [ ] must be a float, given.
- Cache value for key [ ] must be a string, given.
- Cache value for key [ ] must be an array, given.
AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11).
Data as JSON: /api/errors/b2c045515605e7fa.
Report an issue: GitHub.
Appendix: source
Thrown at src/Illuminate/Cache/CacheManager.php:557
$name = enum_value($name) ?? $this->getDefaultDriver();
unset($this->stores[$name]);
}
/**
* 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;
}
/**
* Set the application instance used by the manager.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return $this
*/
public function setApplication($app)
{
$this->app = $app;View on GitHub (pinned to e0f6eb3518)