{"record":{"id":"c7da5e566389c783","repo":"laravel/framework","slug":"illuminate-container-container-bind-argument","errorCode":null,"errorMessage":"Illuminate\\Container\\Container::bind(): Argument #2 ($concrete) must be of type Closure|string|null","messagePattern":"Illuminate\\\\Container\\\\Container::bind\\(\\): Argument #2 \\(\\$concrete\\) must be of type Closure\\|string\\|null","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/Illuminate/Container/Container.php","lineNumber":381,"sourceCode":"                $abstract, $concrete, $shared\n            );\n        }\n\n        $this->dropStaleInstances($abstract);\n\n        // If no concrete type was given, we will simply set the concrete type to the\n        // abstract type. After that, the concrete type to be registered as shared\n        // without being forced to state their classes in both of the parameters.\n        if (is_null($concrete)) {\n            $concrete = $abstract;\n        }\n\n        // If the factory is not a Closure, it means it is just a class name which is\n        // bound into this container to the abstract type and we will just wrap it\n        // up inside its own Closure to give us more convenience when extending.\n        if (! $concrete instanceof Closure) {\n            if (! is_string($concrete)) {\n                throw new TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null');\n            }\n\n            $concrete = $this->getClosure($abstract, $concrete);\n        }\n\n        $this->bindings[$abstract] = ['concrete' => $concrete, 'shared' => $shared];\n\n        // If the abstract type was already resolved in this container we'll fire the\n        // rebound listener so that any objects which have already gotten resolved\n        // can have their copy of the object updated via the listener callbacks.\n        if ($this->resolved($abstract)) {\n            $this->rebound($abstract);\n        }\n    }\n\n    /**\n     * Get the Closure to be used when building a type.\n     *","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/laravel/framework/blob/e0f6eb3518ac29fbbca8529e97d0df7fc9f24481/src/Illuminate/Container/Container.php#L363-L399","documentation":"A PHP TypeError raised by Container::bind() when the $concrete argument is not a Closure, a string, or null. The container wraps string concretes in a closure and treats null as the abstract itself, so any other type (array, object instance, int) is rejected at the type boundary.","triggerScenarios":"Calling $container->bind('key', ['Class', 'method']); passing an object instance instead of a closure: $container->bind('key', $instance); passing an integer or boolean as concrete.","commonSituations":"Mistaking bind() for instance() / singleton(); passing a callable array where only Closure|string is accepted; copy-paste from a context that used a different DI API.","solutions":["Wrap the callable in a Closure: bind('key', fn ($app) => $app->make(SomeClass::class)).","If you want a single instance, use $container->singleton('key', $concrete) or $container->instance('key', $instance) for a pre-built object.","Pass a class-name string so the container auto-wraps it: bind('abstract', Concrete::class).","If passing null is intended (bind concrete to itself), pass null explicitly rather than omitting."],"exampleFix":"// before\n$app->bind('cache.repo', [RedisRepo::class, 'make']);\n// after\n$app->bind('cache.repo', fn ($app) => $app->make(RedisRepo::class));\n// or for a pre-built object\n$app->instance('cache.repo', $repo);","handlingStrategy":"type-guard","validationCode":"function assertBindableConcrete(mixed $concrete): void\n{\n    if ($concrete !== null && ! $concrete instanceof \\Closure && ! is_string($concrete)) {\n        throw new TypeError('bind(): concrete must be Closure|string|null, got ' . get_debug_type($concrete));\n    }\n}\n\nassertBindableConcrete($concrete);\n$container->bind($abstract, $concrete);","typeGuard":"/** @param mixed $c */\nfunction isBindableConcrete($c): bool\n{\n    return $c === null || $c instanceof \\Closure || is_string($c);\n}","tryCatchPattern":"try {\n    $container->bind($abstract, $concrete);\n} catch (\\TypeError $e) {\n    // convert to a closure / use instance() / singleton() as appropriate\n    throw $e;\n}","preventionTips":["Use instance() when you already have a built object; use singleton() for shared Closure|string.","Always wrap callables in fn ($app) => ... when passing to bind().","Static-analyze service providers with PHPStan and a strict container stub."],"tags":["container","di","type-error","binding"],"backgroundTag":null,"analyzedSha":"e0f6eb3518ac29fbbca8529e97d0df7fc9f24481","analyzedAt":"2026-08-11T20:52:37.562Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}