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

  1. Pass a non-static anonymous Closure to Cache::extend: Cache::extend('mydriver', function ($app, $config) { ... }) without the 'static' prefix.
  2. If using Closure::fromCallable, wrap it: Cache::extend('mydriver', fn (...$args) => $callable(...$args)).
  3. Avoid binding closures from internal/extension classes; re-author as PHP userland closures.
  4. 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

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


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)