Laravel error
Laravel: Target class [Controller] does not exist
The error
Target class [PostController] does not exist.A BindingResolutionException naming a controller means the container built a class from a string and found nothing there. Four ways to arrive at it, with four different fixes.
The error
Illuminate\Contracts\Container\BindingResolutionException
Target class [PostController] does not exist.
What it means
Something asked the service container for a class by name, and the container could not find a class by that name. It is not a routing error, even though a route is usually what triggered it - by the time this is thrown, the router has already handed a string to the container and stepped out of the way.
The name in the brackets is the exact string that was looked up. That is the most useful thing on the page, because comparing it to the class's real fully-qualified name usually ends the investigation.
The four causes
The route names the controller in short form. Until Laravel 8 the router
prepended App\Http\Controllers to controller strings. It no longer does, so
a route carried across from an older codebase now asks for a class in the
global namespace:
// asks the container for \PostController
Route::get('/posts', 'PostController@index');
// asks for the class you actually wrote
Route::get('/posts', [\App\Http\Controllers\PostController::class, 'index']);The second form is worth adopting even where the first still works, because the class name is now a symbol rather than a string: rename the controller and your editor updates the route, instead of a request finding out at three in the morning.
The namespace and the folder disagree. A file at
app/Http/Controllers/Admin/PostController.php must declare
namespace App\Http\Controllers\Admin;. Move a file between folders without
editing its first lines and autoloading stops finding it, while the file sits
there looking correct.
Composer's map is stale. The autoloader works from a generated index. Files created outside your editor's project tooling, or after a checkout that skipped an install, are not in it:
composer dump-autoloadCase. PostsController against PostController, or postController in a
route. Local development on a case-insensitive filesystem finds the file
anyway; the Linux server it deploys to does not, which is why this class of
mistake is so reliably a production-only error.
Reading the message properly
The bracketed name tells you which cause you have before you change anything:
| What the message says | What it means |
|---|---|
Target class [PostController] | No namespace at all - a short-form route string |
Target class [App\Http\Controllers\PostController] | The name is right, so the file, its namespace or the autoload map is wrong |
Target class [App\Contracts\PaymentGateway] | An interface with no binding |
The interface case
This one is not a typo and dump-autoload will not touch it. The container
can construct a concrete class it can reflect on, but an interface has no
implementation to instantiate - it needs to be told:
// AppServiceProvider::register()
$this->app->bind(PaymentGateway::class, StripeGateway::class);If the binding exists and you still see the error, check that the provider is
registered, and that the binding is in register() rather than boot() -
something resolved early enough during boot will look for it before boot()
has run.
Stop it happening again
Use the array or first-class callable syntax for every route, and keep
composer dump-autoload -o in the deploy script. The first turns a whole
category of runtime failures into problems your editor points at; the second
means a deploy never serves a stale map.
composer dump-autoload -o is one of a short list of things a deploy has to do
in the right order, and
the list is short enough to write down. Get
the order wrong and the errors look unrelated to each other.
The Vite manifest error comes from this same mistake.
