Laravel : “Target class [controller] does not exist”
(La solution complète qui marche en 2026 – même si tu es sur Laravel 8, 9, 10 ou 11)
Tu tapes php artisan route:list, tout est propre.
Tu cliques sur un lien ou tu lances un test… et là :
Target class [App\Http\Controllers\UserController] does not exist.
Ou pire, dans les logs :
Target class [UserController] does not exist.
Tu vérifies 10 fois : le fichier existe, le namespace est bon, tu as fait composer dump-autoload… rien n’y fait.
Je suis passé par là au moins 40 fois (projets perso, clients, migrations Laravel 8 → 11).
Voici la checklist définitive 2026, classée par fréquence réelle.
Les 8 causes les plus fréquentes (et comment les tuer en 30 secondes)
1. Laravel 8+ → tu utilises encore la syntaxe ancienne dans routes (80 % des cas)
Avant Laravel 8 :
Route::get('/users', 'UserController@index');
Depuis Laravel 8 (et obligatoire en 9+) :
use App\Http\Controllers\UserController;
Route::get('/users', [UserController::class, 'index']);
Solution : remplace toutes les chaînes par [Controller::class, 'method']
Astuce 2026 : PHPStorm / VSCode + Laravel Idea te fait la conversion en 1 clic (Refactor → Convert String to Class)
2. Tu as oublié le use en haut du fichier routes/api.php ou routes/web.php
Très fréquent quand tu copies-colles une route.
// Mauvais
Route::get('/profile', [ProfileController::class, 'show']);
// Bon (si pas de use)
Route::get('/profile', [\App\Http\Controllers\ProfileController::class, 'show']);
Ou ajoute simplement en haut :
use App\Http\Controllers\ProfileController;
3. Le controller n’est pas dans le bon namespace
Laravel attend par défaut App\Http\Controllers
Si tu l’as mis ailleurs (ex: App\Http\Controllers\Admin\DashboardController) :
Route::get('/admin', [App\Http\Controllers\Admin\DashboardController::class, 'index']);
Ou crée un alias dans RouteServiceProvider.php (Laravel 9+) :
protected $namespace = 'App\Http\Controllers'; // déjà commenté par défaut
4. Cache de config/routes activé (très fréquent en prod)
Tu as modifié les routes, mais Laravel lit encore l’ancien cache.
Solution (30 sec) :
php artisan route:clear
php artisan config:clear
php artisan cache:clear
php artisan view:clear
Ou tout en une ligne :
php artisan optimize:clear
5. Tu es en Laravel 11 (nouvelle structure sans app/Http/Controllers par défaut)
Laravel 11 a changé la structure : les controllers sont dans app/Controllers ou directement à la racine app/.
Solution : déplace ton controller dans app/Controllers et ajuste le namespace :
namespace App\Controllers;
class UserController extends Controller { ... }
Et dans les routes :
use App\Controllers\UserController;
6. Tu utilises un préfixe ou un namespace dans RouteServiceProvider
Très fréquent sur les projets migrés de Laravel 7/8 vers 10/11.
Ouvre app/Providers/RouteServiceProvider.php :
// Si tu vois ça, c’est la source du mal :
protected $namespace = 'App\\Http\\Controllers';
// Supprime ou commente cette ligne (Laravel 9+ n’en a plus besoin)
Puis :
php artisan route:clear
7. Tu as un typo dans le nom de classe ou de méthode
UserContoller(un “l” en trop)indxeau lieu deindex
Solution rapide : lance cette commande magique (Laravel 9+) :
php artisan route:list --name=users
Elle te dira exactement quel controller/method Laravel cherche.
8. Composer n’a pas autoloadé ton nouveau controller
composer dump-autoload
# ou si tu es parano :
rm -rf bootstrap/cache/*
composer dump-autoload -o
Ma checklist “zéro prise de tête” que je colle dans tous mes projets Laravel 10/11
Dans routes/web.php et routes/api.php :
<?php
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\DashboardController;
use App\Http\Controllers\UserController;
// etc.
Route::middleware('auth')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
Route::resource('users', UserController::class);
});
Et je n’utilise jamais les strings.
Bonus 2026 : le script qui corrige tout automatiquement
Crée un fichier fix-routes.php à la racine :
<?php
require 'vendor/autoload.php';
$app = require_once 'bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();
use Illuminate\Support\Facades\Route;
Route::get('/fix-routes', function () {
\Artisan::call('route:clear');
\Artisan::call('config:clear');
\Artisan::call('cache:clear');
return "Routes & config vidés – tout devrait remarcher !";
});
Visite tonsite.com/fix-routes une fois → problème réglé.
Récap ultra-rapide
| Cause | Solution en 1 ligne 2026 | Temps |
|---|---|---|
| Syntaxe ancienne dans routes | Passer à [Controller::class, 'method'] | 2 min |
| Pas de use en haut | Ajouter le use ou \App…\Controller::class | 10 sec |
| Cache routes/config | php artisan optimize:clear | 5 sec |
| Namespace changé (Laravel 11) | Controller dans app/Controllers | 1 min |
| Préfixe namespace dans RouteServiceProvider | Supprimer la ligne $namespace | 30 sec |
Tu as résolu le problème comment ?
Laravel 8 ? 10 ? 11 ? Dis-moi en commentaire ta version + ce qui a marché chez toi.
Ça aidera des centaines de devs qui tombent sur cet article à 3 h du matin en sueur.
Distribue-le à ton collègue qui t’envoie “ça marche en local mais pas en prod” pour la 5ᵉ fois cette semaine.
Tu veux comprendre l’étape suivante ? Node.js : “ERR_SSL_VERSION_OR_CIPHER_MISMATCH”
Tu peux aussi lire ceci pour mieux comprendre : Basic Routing