{"record":{"id":"b2c045515605e7fa","repo":"laravel/framework","slug":"unable-to-bind-custom-driver-callback-b2c045","errorCode":null,"errorMessage":"Unable to bind custom driver callback","messagePattern":"Unable to bind custom driver callback","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Illuminate/Cache/CacheManager.php","lineNumber":557,"sourceCode":"        $name = enum_value($name) ?? $this->getDefaultDriver();\n\n        unset($this->stores[$name]);\n    }\n\n    /**\n     * Register a custom driver creator Closure.\n     *\n     * @param  string  $driver\n     * @param  \\Closure  $callback\n     *\n     * @param-closure-this  $this  $callback\n     *\n     * @return $this\n     */\n    public function extend($driver, Closure $callback)\n    {\n        try {\n            $callback = $this->bindCallbackToSelf($callback) ?? throw new RuntimeException('Unable to bind custom driver callback');\n        } catch (ReflectionException $e) {\n            throw new RuntimeException('Unable to bind custom driver callback', previous: $e);\n        }\n\n        $this->customCreators[$driver] = $callback;\n\n        return $this;\n    }\n\n    /**\n     * Set the application instance used by the manager.\n     *\n     * @param  \\Illuminate\\Contracts\\Foundation\\Application  $app\n     * @return $this\n     */\n    public function setApplication($app)\n    {\n        $this->app = $app;","sourceCodeStart":539,"sourceCodeEnd":575,"githubUrl":"https://github.com/laravel/framework/blob/e0f6eb3518ac29fbbca8529e97d0df7fc9f24481/src/Illuminate/Cache/CacheManager.php#L539-L575","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nCache::extend('mydriver', \\Closure::fromCallable([MyDriverFactory::class, 'make']));\n\n// after\nCache::extend('mydriver', function ($app, array $config) {\n    return Cache::repository(new MyDriverStore($config));\n});","handlingStrategy":"validation","validationCode":"// Validate the closure is a non-static anonymous Closure before extending\n$callback = function ($app, array $config) {\n    return Cache::repository(new MyDriverStore($config));\n};\n$reflector = new \\ReflectionFunction($callback);\nif (! $reflector->isAnonymous() || $reflector->isStatic()) {\n    throw new \\LogicException('Cache::extend expects a non-static anonymous Closure.');\n}\nCache::extend('mydriver', $callback);","typeGuard":"function isBindableAnonymousClosure(\\Closure $closure): bool\n{\n    $r = new \\ReflectionFunction($closure);\n    return $r->isAnonymous() && ! $r->isStatic();\n}","tryCatchPattern":"try {\n    Cache::extend('mydriver', $callback);\n} catch (\\RuntimeException $e) {\n    if ($e->getMessage() === 'Unable to bind custom driver callback') {\n        // re-author as a plain anonymous closure and retry\n        Cache::extend('mydriver', function ($app, array $config) use ($callback) {\n            return $callback($app, $config);\n        });\n    } else {\n        throw $e;\n    }\n}","preventionTips":["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."],"tags":["cache","extend","closures","reflection"],"backgroundTag":null,"analyzedSha":"e0f6eb3518ac29fbbca8529e97d0df7fc9f24481","analyzedAt":"2026-08-11T20:52:37.562Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}