laravel/framework · error · TypeError
Illuminate\Container\Container::bind(): Argument #2 ($concre
Error message
Illuminate\Container\Container::bind(): Argument #2 ($concrete) must be of type Closure|string|null
What it means
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.
Source
Thrown at src/Illuminate/Container/Container.php:381
$abstract, $concrete, $shared
);
}
$this->dropStaleInstances($abstract);
// If no concrete type was given, we will simply set the concrete type to the
// abstract type. After that, the concrete type to be registered as shared
// without being forced to state their classes in both of the parameters.
if (is_null($concrete)) {
$concrete = $abstract;
}
// If the factory is not a Closure, it means it is just a class name which is
// bound into this container to the abstract type and we will just wrap it
// up inside its own Closure to give us more convenience when extending.
if (! $concrete instanceof Closure) {
if (! is_string($concrete)) {
throw new TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null');
}
$concrete = $this->getClosure($abstract, $concrete);
}
$this->bindings[$abstract] = ['concrete' => $concrete, 'shared' => $shared];
// If the abstract type was already resolved in this container we'll fire the
// rebound listener so that any objects which have already gotten resolved
// can have their copy of the object updated via the listener callbacks.
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
}
/**
* Get the Closure to be used when building a type.
*View on GitHub (pinned to e0f6eb3518)
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.
Example fix
// before
$app->bind('cache.repo', [RedisRepo::class, 'make']);
// after
$app->bind('cache.repo', fn ($app) => $app->make(RedisRepo::class));
// or for a pre-built object
$app->instance('cache.repo', $repo); Defensive patterns
Strategy: type-guard
Validate before calling
function assertBindableConcrete(mixed $concrete): void
{
if ($concrete !== null && ! $concrete instanceof \Closure && ! is_string($concrete)) {
throw new TypeError('bind(): concrete must be Closure|string|null, got ' . get_debug_type($concrete));
}
}
assertBindableConcrete($concrete);
$container->bind($abstract, $concrete); Type guard
/** @param mixed $c */
function isBindableConcrete($c): bool
{
return $c === null || $c instanceof \Closure || is_string($c);
} Try / catch
try {
$container->bind($abstract, $concrete);
} catch (\TypeError $e) {
// convert to a closure / use instance() / singleton() as appropriate
throw $e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Target [$concrete] is not instantiable while building [$prev
- Target [$concrete] is not instantiable.
- Unresolvable dependency resolving [$parameter] in class {$pa
- Method not provided.
- Unable to resolve dependency [{$parameter}] in class {$param
AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11).
Data as JSON: /api/errors/c7da5e566389c783.
Report an issue: GitHub.