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

The provided class "%s" is an enum, and cannot be instantiat

Error message

The provided class "%s" is an enum, and cannot be instantiated

What it means

Thrown by fromEnum() when getReflectionClass() (src/Instantiator.php:147-148) detects via enum_exists($className, false) that the requested type is a PHP 8.1+ enum. Enums have no instances beyond their declared cases, so they cannot be constructor-less instantiated either; the library rejects them before any reflection work. The correct construction path is accessing a case (Suit::Hearts) or Suit::from($value), never the instantiator.

Source

Thrown at src/Exception/InvalidArgumentException.php:48

    }

    /**
     * @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(),
        ));
    }

    public static function fromEnum(string $className): self
    {
        return new self(sprintf(
            'The provided class "%s" is an enum, and cannot be instantiated',
            $className,
        ));
    }
}

View on GitHub (pinned to cbb879d6ee)

Solutions

  1. Remove the enum from the set of names passed to instantiate() and construct it directly: use Suit::Hearts, Suit::cases(), or Suit::from($value).
  2. Filter enum names out of dynamic type lists with enum_exists($name, false) before calling the instantiator.
  3. If a config table or manifest still references the old class name after an enum migration, update those entries to point at the new usage pattern.

Example fix

// before
$suit = $instantiator->instantiate(Suit::class);
// InvalidArgumentException: The provided class "Suit" is an enum,
// and cannot be instantiated

// after: enums are constructed from their cases, not instantiated
$suit = Suit::Hearts;
// or from a persisted value
$suit = Suit::from($storedValue);
Defensive patterns

Strategy: validation

Validate before calling

use function enum_exists;

if (enum_exists($className, false)) {
    throw new LogicException(sprintf('%s is an enum: construct it from a case or ::from().', $className));
}

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

Type guard

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

Try / catch

use Doctrine\Instantiator\Exception\InvalidArgumentException;

try {
    $instance = $instantiator->instantiate($className);
} catch (InvalidArgumentException $e) {
    if (str_contains($e->getMessage(), 'is an enum')) {
        // enums are value types: build them from cases, not instantiation
        return $className::cases()[0];
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling (new Instantiator())->instantiate(Suit::class) or any native enum FQCN on PHP >= 8.1. enum_exists() returns true at src/Instantiator.php:147 and fromEnum($className) throws. Common when generic factories, entity metadata walkers, or serializer/proxy generators pass every type name they discover into instantiate().

Common situations: A constants-holder class was migrated to a native enum during a PHP 8.1 upgrade while a config file or database table still lists it among instantiable classes; dependency graph walkers (ORM proxies, data-fixture loaders) enumerate all declared types including enums; hybrid code supporting both PHP < 8.1 (no enums) and >= 8.1 hits the enum path only in newer environments.

Related errors


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