symfony/routing · error · LogicException
The " :: ()" method must return a RouteCollection: " "…
Error message
The "%s::%s()" method must return a RouteCollection: "%s" returned.
What it means
ObjectLoader::load() calls a loader method on a routing resource object (a class with #[Route] attributes or a load() method) and requires it to return a RouteCollection. Symfony throws this LogicException when the method returns something else (null, an array, another object). This is an internal contract between the router and resource loader classes.
Solutions
- Ensure the loader method returns a RouteCollection on every code path: $collection = new RouteCollection(); ...; return $collection;
- Fix methods that return $collection->all() or an iterator — return the RouteCollection itself
- Add a RouteCollection return type declaration to the method so PHP enforces the contract at runtime
- If the method legitimately has no routes, return an empty new RouteCollection() instead of null
Example fix
// before
public function loadRoutes(): array
{
return $this->routes->all();
}
// after
public function loadRoutes(): RouteCollection
{
return $this->routes;
} Defensive patterns
Strategy: validation
Validate before calling
if (!$routes instanceof \Symfony\Component\Routing\RouteCollection) { throw new \LogicException('Loader must return a RouteCollection'); } Type guard
function isRouteCollection(mixed $v): bool { return $v instanceof \Symfony\Component\Routing\RouteCollection; } Try / catch
try { $collection = $loaderObject->load($resource, $env); } catch (\LogicException $e) { // log loader class/method, fix return type } Prevention
- Declare RouteCollection as the return type on every loader method
- Return an empty RouteCollection instead of null when there are no routes
- Add a unit test asserting each loader method returns RouteCollection
When it happens
Trigger: A resource class's load(Object $resource, ?string $type = null) method (or a method named by the resource type) returns null early, returns an array instead of RouteCollection, or has a typo in return path that skips the return statement.
Common situations: Hand-written resource loader classes for annotation/attribute routing; a custom loader method accidentally returning $routes->all() (an array) or returning nothing on an early-exit branch; refactoring that changed the return type without updating all returns.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Parameter " " for route " " must match " " (" " given) to…
- Parameters for route
- Route aliases cannot be used on non-invokable class
- The " ()" method must not be called.
- The return value in config file
AI-assisted analysis of symfony/routing@83fa223250 (2026-09-14).
Data as JSON: /api/errors/2b51bbe2420520d9.
Report an issue: GitHub.
Appendix: source
Thrown at Loader/ObjectLoader.php:56
if (!preg_match('/^[^\:]+(?:::(?:[^\:]+))?$/', $resource)) {
throw new \InvalidArgumentException(\sprintf('Invalid resource "%s" passed to the %s route loader: use the format "object_id::method" or "object_id" if your object class has an "__invoke" method.', $resource, \is_string($type) ? '"'.$type.'"' : 'object'));
}
$parts = explode('::', $resource);
$method = $parts[1] ?? '__invoke';
$loaderObject = $this->getObject($parts[0]);
if (!\is_callable([$loaderObject, $method])) {
throw new \BadMethodCallException(\sprintf('Method "%s" not found on "%s" when importing routing resource "%s".', $method, get_debug_type($loaderObject), $resource));
}
$routeCollection = $loaderObject->$method($this, $this->env);
if (!$routeCollection instanceof RouteCollection) {
$type = get_debug_type($routeCollection);
throw new \LogicException(\sprintf('The "%s::%s()" method must return a RouteCollection: "%s" returned.', get_debug_type($loaderObject), $method, $type));
}
// make the object file tracked so that if it changes, the cache rebuilds
$this->addClassResource(new \ReflectionClass($loaderObject), $routeCollection);
return $routeCollection;
}
private function addClassResource(\ReflectionClass $class, RouteCollection $collection): void
{
do {
if (is_file($class->getFileName())) {
$collection->addResource(new FileResource($class->getFileName()));
}
} while ($class = $class->getParentClass());
}
}
View on GitHub (pinned to 83fa223250)