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
- Pass the concrete class that uses the trait instead of the trait itself, e.g. instantiate(User::class) rather than AuditableTrait::class.
- Filter type lists before instantiation: skip any name where trait_exists($name) is true.
- 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
- Keep traits and classes in separate config sections so trait names never reach type-to-instance factories.
- Filter any dynamic type list with class_exists() before instantiating.
- After moving code into a trait, update call sites that still reference the old class name.
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
- The provided type "%s" is an interface, and cannot be instan
- The provided class "%s" is abstract, and cannot be instantia
- The provided class "%s" is an enum, and cannot be instantiat
- The provided class "%s" does not exist
- An exception was raised while trying to instantiate an insta
AI-assisted analysis of doctrine/instantiator@cbb879d6ee (2026-08-21).
Data as JSON: /api/errors/e30be6f8846b9667.
Report an issue: GitHub.