symfony/routing · error · InvalidArgumentException
Route aliases cannot be used on non-invokable class
Error message
Route aliases cannot be used on non-invokable class "%s".
What it means
AttributeClassLoader rejects #[AsAlias]/alias attributes applied at the class level when the controller class has no __invoke method. Route aliases are only meaningful on an invokable class or on individual methods, because aliases point to a single route.
Solutions
- Add an __invoke() method to the class so it is invokable.
- Move the aliases argument to a method-level #[Route] attribute instead.
- Remove the aliases argument if no alias is needed.
Example fix
// before
#[Route('/x', name: 'x', aliases: ['x_alias'])]
class XController { public function show() {...} }
// after
#[Route('/x', name: 'x', aliases: ['x_alias'])]
class XController { public function __invoke() {...} } Defensive patterns
Strategy: validation
Validate before calling
if (!$ref->hasMethod('__invoke')) { foreach ($ref->getAttributes(\Symfony\Component\Routing\Attribute\Route::class) as $a) { if (!empty($a->getArguments()['aliases'])) { throw new \LogicException('aliases require __invoke'); } } } Type guard
function isInvokableController(string $class): bool { return method_exists($class, '__invoke'); } Try / catch
try { $loader->load($class); } catch (\InvalidArgumentException $e) { if (str_contains($e->getMessage(), 'Route aliases cannot be used')) { /* fix config or rethrow */ throw $e; } throw $e; } Prevention
- Only place aliases on __invoke controllers or method-level Route attributes.
- Run bin/console cache:warmup or debug:router in CI to catch attribute misconfigurations early.
- Code-review rule: class-level #[Route] without __invoke must not carry aliases.
When it happens
Trigger: Loading a controller class (e.g. #[Route(...)] with aliases: ['name']) that has no __invoke(); running cache:warmup or the app so the class is loaded through the attribute loader.
Common situations: Refactoring a method-controller to a class controller and copying the aliases argument to the class-level attribute; typos placing aliases on #[Route] at class level.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Parameter " " for route " " must match " " (" " given) to…
- Parameters for route
- The " ()" method must not be called.
- The return value in config file
- Namespace " " is not a valid PSR-4 prefix.
AI-assisted analysis of symfony/routing@83fa223250 (2026-09-14).
Data as JSON: /api/errors/ad12156797c30d35.
Report an issue: GitHub.
Appendix: source
Thrown at Loader/AttributeClassLoader.php:99
}
$class = new \ReflectionClass($class);
if ($class->isAbstract()) {
throw new \InvalidArgumentException(\sprintf('Attributes from class "%s" cannot be read as it is abstract.', $class->getName()));
}
$globals = $this->getGlobals($class);
$collection = new RouteCollection();
$collection->addResource(new ReflectionClassResource($class));
if ($globals['env'] && !\in_array($this->env, $globals['env'], true)) {
return $collection;
}
$fqcnAlias = false;
if (!$class->hasMethod('__invoke')) {
foreach ($this->getAttributes($class) as $attr) {
if ($attr->aliases) {
throw new InvalidArgumentException(\sprintf('Route aliases cannot be used on non-invokable class "%s".', $class->getName()));
}
}
}
foreach ($class->getMethods() as $method) {
$this->defaultRouteIndex = 0;
$routeNamesBefore = array_keys($collection->all());
foreach ($this->getAttributes($method) as $attr) {
$this->addRoute($collection, $attr, $globals, $class, $method);
if ('__invoke' === $method->name) {
$fqcnAlias = true;
}
}
if (1 === $collection->count() - \count($routeNamesBefore)) {
$newRouteName = current(array_diff(array_keys($collection->all()), $routeNamesBefore));
if ($newRouteName !== $aliasName = \sprintf('%s::%s', $class->name, $method->name)) {
$collection->addAlias($aliasName, $newRouteName);View on GitHub (pinned to 83fa223250)