{"record":{"id":"906be6241b135293","repo":"doctrine/orm","slug":"s-s-must-have-a-type-when-used-with-typednodef","errorCode":null,"errorMessage":"%s::$%s must have a type when used with TypedNoDefaultPropertyAccessor","messagePattern":"(.+?)::\\$(.+?) must have a type when used with TypedNoDefaultPropertyAccessor","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Mapping/PropertyAccessors/TypedNoDefaultPropertyAccessor.php","lineNumber":22,"sourceCode":"\nnamespace Doctrine\\ORM\\Mapping\\PropertyAccessors;\n\nuse Closure;\nuse InvalidArgumentException;\nuse ReflectionProperty;\n\nuse function assert;\nuse function sprintf;\n\n/** @internal */\nclass TypedNoDefaultPropertyAccessor implements PropertyAccessor\n{\n    private Closure|null $unsetter = null;\n\n    public function __construct(private PropertyAccessor $parent, private ReflectionProperty $reflectionProperty)\n    {\n        if (! $this->reflectionProperty->hasType()) {\n            throw new InvalidArgumentException(sprintf(\n                '%s::$%s must have a type when used with TypedNoDefaultPropertyAccessor',\n                $this->reflectionProperty->getDeclaringClass()->getName(),\n                $this->reflectionProperty->getName(),\n            ));\n        }\n\n        if ($this->reflectionProperty->getType()->allowsNull()) {\n            throw new InvalidArgumentException(sprintf(\n                '%s::$%s must not be nullable when used with TypedNoDefaultPropertyAccessor',\n                $this->reflectionProperty->getDeclaringClass()->getName(),\n                $this->reflectionProperty->getName(),\n            ));\n        }\n    }\n\n    public function setValue(object $object, mixed $value): void\n    {\n        if ($value === null) {","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/doctrine/orm/blob/d9b9ff73016bf598ae07515f97289ce8074e97a5/src/Mapping/PropertyAccessors/TypedNoDefaultPropertyAccessor.php#L4-L40","documentation":"TypedNoDefaultPropertyAccessor implements the 'typed property without default value' strategy: on setValue(null) it unsets the property so PHP reports it as uninitialized rather than null. That trick only works for declared, non-nullable types, so the constructor requires the ReflectionProperty to have a type and throws InvalidArgumentException otherwise. PropertyAccessorFactory only wraps typed non-nullable properties, so this fires on direct/custom instantiation.","triggerScenarios":"Directly calling new TypedNoDefaultPropertyAccessor($parent, $reflectionProperty) for a property declared without a type (legacy `public $foo;`) — the untyped/nullable case is explicitly rejected by the constructor.","commonSituations":"Custom property-accessor chains that wrap every property unconditionally; legacy entities predating typed properties; code copied from PropertyAccessorFactory without the hasType()/allowsNull() guard.","solutions":["Only wrap typed, non-nullable properties; add if (! $refl->hasType() || $refl->getType()->allowsNull()) skip.","Prefer PropertyAccessorFactory::createPropertyAccessor(), which applies the wrapping conditionally.","Add a native type declaration to the property if it should participate in this strategy."],"exampleFix":"// before\n$accessor = new TypedNoDefaultPropertyAccessor($inner, $refl); // untyped $refl -> InvalidArgumentException\n\n// after\n$accessor = ($refl->hasType() && ! $refl->getType()->allowsNull())\n    ? new TypedNoDefaultPropertyAccessor($inner, $refl)\n    : $inner;","handlingStrategy":"validation","validationCode":"$accessor = ($refl->hasType() && ! $refl->getType()->allowsNull())\n    ? new TypedNoDefaultPropertyAccessor($inner, $refl)\n    : $inner;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use PropertyAccessorFactory instead of hand-built chains.","Add native type declarations to entity properties before relying on unset-based strategies.","Cover custom accessors with unit tests against typed/untyped properties."],"tags":["doctrine-orm","property-accessor","typed-properties","internal-api"],"backgroundTag":"typed-property-requirement","analyzedSha":"d9b9ff73016bf598ae07515f97289ce8074e97a5","analyzedAt":"2026-08-21T06:13:15.863Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}