doctrine/instantiator · error · Doctrine\Instantiator\Exception\InvalidArgumentException

The provided type "%s" is a trait, and cannot be instantiate

Error message

The provided type "%s" is a trait, and cannot be instantiated

What it means

doctrine/instantiator builds constructor-less instances only for concrete classes: its gate in getReflectionClass() (src/Instantiator.php:143) requires class_exists(), which is false for traits. fromNonExistingClass() then checks trait_exists() and, finding the name is a trait, reports that it cannot be instantiated. The error indicates the string passed to Instantiator::instantiate() is a trait name rather than a class name.

Source

Thrown at src/Exception/InvalidArgumentException.php:26

use ReflectionClass;

use function interface_exists;
use function sprintf;
use function trait_exists;

/**
 * Exception for invalid arguments provided to the instantiator
 */
class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface
{
    public static function fromNonExistingClass(string $className): self
    {
        if (interface_exists($className)) {
            return new self(sprintf('The provided type "%s" is an interface, and cannot be instantiated', $className));
        }

        if (trait_exists($className)) {
            return new self(sprintf('The provided type "%s" is a trait, and cannot be instantiated', $className));
        }

        return new self(sprintf('The provided class "%s" does not exist', $className));
    }

    /**
     * @phpstan-param ReflectionClass<T> $reflectionClass
     *
     * @template T of object
     */
    public static function fromAbstractClass(ReflectionClass $reflectionClass): self
    {
        return new self(sprintf(
            'The provided class "%s" is abstract, and cannot be instantiated',
            $reflectionClass->getName(),
        ));
    }

View on GitHub (pinned to cbb879d6ee)

Solutions

  1. Pass the concrete class that uses the trait instead of the trait itself, e.g. instantiate(User::class) rather than AuditableTrait::class.
  2. Filter type lists before instantiation: skip any name where trait_exists($name) is true.
  3. If the name arrives dynamically (config, DB), validate it with class_exists($name) before calling instantiate() and fail with a clear message naming the offending entry.

Example fix

// before
$object = $instantiator->instantiate(AuditableTrait::class);
// InvalidArgumentException: The provided type "AuditableTrait" is a trait,
// and cannot be instantiated

// after: instantiate a class that consumes the trait
$object = $instantiator->instantiate(User::class);
Defensive patterns

Strategy: validation

Validate before calling

use function trait_exists;

if (trait_exists($className)) {
    throw new LogicException(sprintf('%s is a trait and cannot be instantiated.', $className));
}

$object = (new Instantiator())->instantiate($className);

Type guard

function isInstantiableClassName(string $className): bool
{
    return class_exists($className) && ! trait_exists($className, false);
}

Try / catch

use Doctrine\Instantiator\Exception\InvalidArgumentException;

try {
    $instance = $instantiator->instantiate($className);
} catch (InvalidArgumentException $e) {
    if (str_contains($e->getMessage(), 'is a trait')) {
        // the name pointed at a trait: locate the concrete class that uses it
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling (new Instantiator())->instantiate(AuditableTrait::class) or any trait FQCN. Traits fail class_exists(), so fromNonExistingClass() executes; trait_exists($className) returns true and the trait message variant is thrown. Happens when fixture factories, hydration frameworks, or code-generation tools feed a list of declared types into instantiate() without filtering traits.

Common situations: A configuration or mapping table stores trait names alongside class names and the wrong row is selected; refactor moved behavior into a trait but a call site still passes the old name; generic tooling iterates all declared types (class_exists || interface_exists || trait_exists) from a manifest and passes traits to the instantiator.

Related errors


AI-assisted analysis of doctrine/instantiator@cbb879d6ee (2026-08-21). Data as JSON: /api/errors/e30be6f8846b9667. Report an issue: GitHub.