doctrine/orm · error · LogicException
The attribute "%s" is repeatable. Call getPropertyAttributeC
Error message
The attribute "%s" is repeatable. Call getPropertyAttributeCollection() instead.
What it means
AttributeReader is Doctrine ORM's internal wrapper over native PHP attributes. getPropertyAttribute() returns exactly one MappingAttribute instance, so it refuses attribute classes declared with Attribute::IS_REPEATABLE (e.g. Index, UniqueConstraint, AssociationOverride, AttributeOverride), where one property may carry several instances. For those it throws LogicException and directs you to getPropertyAttributeCollection().
Source
Thrown at src/Mapping/Driver/AttributeReader.php:66
*
* @template T of MappingAttribute
*/
public function getPropertyAttributes(ReflectionProperty $property): array
{
return $this->convertToAttributeInstances($property->getAttributes());
}
/**
* @param class-string<T> $attributeName The name of the annotation.
*
* @return T|null
*
* @template T of MappingAttribute
*/
public function getPropertyAttribute(ReflectionProperty $property, string $attributeName)
{
if ($this->isRepeatable($attributeName)) {
throw new LogicException(sprintf(
'The attribute "%s" is repeatable. Call getPropertyAttributeCollection() instead.',
$attributeName,
));
}
return $this->getPropertyAttributes($property)[$attributeName] ?? null;
}
/**
* @param class-string<T> $attributeName The name of the annotation.
*
* @return RepeatableAttributeCollection<T>
*
* @template T of MappingAttribute
*/
public function getPropertyAttributeCollection(
ReflectionProperty $property,
string $attributeName,View on GitHub (pinned to d9b9ff7301)
Solutions
- Call getPropertyAttributeCollection($property, $attribute) and iterate the returned RepeatableAttributeCollection (an ArrayObject holding every instance) instead.
- For non-repeatable attributes (Column, JoinColumn, OneToMany, ...) keep using getPropertyAttribute().
- If your custom attribute was not meant to be repeatable, remove Attribute::IS_REPEATABLE from its #[Attribute] declaration.
Example fix
// before
$attr = $reader->getPropertyAttribute($property, Mapping\Index::class); // LogicException: repeatable
// after
/** @var RepeatableAttributeCollection<Mapping\Index> $indexes */
$indexes = $reader->getPropertyAttributeCollection($property, Mapping\Index::class);
foreach ($indexes as $index) { /* ... */ } Defensive patterns
Strategy: type-guard
Type guard
/** @param class-string $attribute */
function isRepeatableAttribute(string $attribute): bool
{
$flags = (new ReflectionClass($attribute))
->getAttributes(Attribute::class)[0]?->getArguments()[0] ?? 0;
return ($flags & Attribute::IS_REPEATABLE) !== 0;
}
$attrs = isRepeatableAttribute($name)
? $reader->getPropertyAttributeCollection($property, $name)
: (null !== ($a = $reader->getPropertyAttribute($property, $name)) ? [$a] : []); Prevention
- Prefer getClassAttributes()/getPropertyAttributes() which return both shapes and let you inspect.
- Check the #[Attribute] declaration before choosing the singular vs collection getter.
- Remember AttributeReader is @internal — pin exact ORM versions when you use it.
When it happens
Trigger: Calling AttributeReader::getPropertyAttribute($reflectionProperty, $attribute) where $attribute is a repeatable mapping attribute such as Doctrine\ORM\Mapping\Index, UniqueConstraint, AssociationOverride or AttributeOverride attached to a property.
Common situations: Custom tooling or bundles reusing Doctrine's AttributeReader to inspect entity mapping attributes; code ported from the old doctrine/annotations AnnotationReader where a single getAttribute()-style call was generic; PHPStan/PSalm plugins walking mapping attributes.
Related errors
- The attribute "%s" is not repeatable. Call getPropertyAttrib
- Undefined method "%s". The method name must start with eithe
- %s::$%s must be readonly property
- %s::$%s must have a type when used with TypedNoDefaultProper
- %s::$%s must not be nullable when used with TypedNoDefaultPr
AI-assisted analysis of doctrine/orm@d9b9ff7301 (2026-08-21).
Data as JSON: /api/errors/8a38e96a478d140d.
Report an issue: GitHub.