symfony/routing · error · InvalidArgumentException

Attributes from class

Error message

Attributes from class "%s" cannot be read as it is abstract.

What it means

AttributeClassLoader::load() rejects abstract classes because attributes (Route/condition decorators) on an abstract class cannot yield an instantiable controller route set in the normal way; reading them is considered a configuration mistake.

Solutions

  1. Load a concrete (non-abstract) controller class
  2. Exclude abstract classes from your controller-discovery list (e.g. instanceof / isAbstract filter)
  3. Move route attributes from the abstract base to concrete subclasses, or use a parent-class-aware loader

Example fix

// before
$loader->load(AbstractApiController::class); // abstract
// after
$loader->load(ProductApiController::class); // concrete subclass
Defensive patterns

Strategy: validation

Validate before calling

$ref = new \ReflectionClass($class);
if ($ref->isAbstract()) {
    throw new \InvalidArgumentException("Cannot load attributes from abstract class '$class'");
}

Type guard

function isConcreteClass(string $class): bool {
    return class_exists($class) && !(new \ReflectionClass($class))->isAbstract();
}

Try / catch

try {
    $collection = $loader->load($class);
} catch (\InvalidArgumentException $e) {
    // skip abstract classes discovered by the scanner
}

Prevention

When it happens

Trigger: $loader->load(AbstractControllerBase::class) where the class is declared abstract — e.g. pointing the loader at a shared base controller instead of a concrete one.

Common situations: Loading a base controller class with common route attributes; auto-discovery mistakenly including abstract classes in the controller scan list.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of symfony/routing@83fa223250 (2026-09-14). Data as JSON: /api/errors/8045e8a86d02c9cc. Report an issue: GitHub.

Appendix: source

Thrown at Loader/AttributeClassLoader.php:85

     * Sets the attribute class to read route properties from.
     */
    public function setRouteAttributeClass(string $class): void
    {
        $this->routeAttributeClass = $class;
    }

    /**
     * @throws \InvalidArgumentException When route can't be parsed
     */
    public function load(mixed $class, ?string $type = null): RouteCollection
    {
        if (!class_exists($class)) {
            throw new \InvalidArgumentException(\sprintf('Class "%s" does not exist.', $class));
        }

        $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()));
                }
            }
        }

View on GitHub (pinned to 83fa223250)