laravel/framework · error · RuntimeException

Unable to bind custom driver callback

Error message

Unable to bind custom driver callback

What it means

CacheManager::extend() calls bindCallbackToSelf() to rebind the custom creator closure to the manager instance; if that returns null it throws RuntimeException. This path indicates the closure could not be bound (e.g. a static closure or a Closure that cannot be rebound to $this).

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 bd6b5437e6)

Solutions

  1. Use a non-static closure: Cache::extend('mydriver', function ($app, $config) { ... });.
  2. Avoid 'static fn' syntax when registering extend callbacks; the manager must be able to bind $this.
  3. After changing the closure, run config:clear so the provider re-registers.

Example fix

// before
Cache::extend('mydriver', static function ($app, $config) {
    return new Repository(new MyStore());
});

// after
Cache::extend('mydriver', function ($app, $config) {
    return new Repository(new MyStore());
});
Defensive patterns

Strategy: validation

Validate before calling

$callback = function ($app, $config) { return new \Illuminate\Cache\Repository(new MyStore()); };
$reflection = new \ReflectionFunction($callback);
if ($reflection->isStatic()) {
    throw new \LogicException('Cache::extend() requires a non-static closure.');
}
Cache::extend('mydriver', $callback);

Type guard

function isBindableClosure(\Closure $c): bool
{
    return ! (new \ReflectionFunction($c))->isStatic();
}

Try / catch

try {
    Cache::extend('mydriver', $cb);
} catch (\RuntimeException $e) {
    if ($e->getMessage() === 'Unable to bind custom driver callback') {
        // replace static closure with a normal closure
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling Cache::extend('mydriver', static function (...)) with a static closure that cannot be bound to an object instance. Passing a Closure created from an arrow function or a non-bindable context.

Common situations: Writing a custom cache driver registration with a static closure out of habit. Refactoring a closure to static fn () => ... which breaks rebinding.

Related errors


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