{"record":{"id":"4b9043180c521d89","repo":"doctrine/orm","slug":"s-s-must-not-be-nullable-when-used-with-typedn","errorCode":null,"errorMessage":"%s::$%s must not be nullable when used with TypedNoDefaultPropertyAccessor","messagePattern":"(.+?)::\\$(.+?) must not be nullable when used with TypedNoDefaultPropertyAccessor","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Mapping/PropertyAccessors/TypedNoDefaultPropertyAccessor.php","lineNumber":30,"sourceCode":"use 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) {\n            if ($this->unsetter === null) {\n                $propertyName   = $this->reflectionProperty->getName();\n                $this->unsetter = function () use ($propertyName): void {\n                    unset($this->$propertyName);\n                };\n            }\n\n            $unsetter = $this->unsetter->bindTo($object, $this->reflectionProperty->getDeclaringClass()->getName());","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/doctrine/orm/blob/d9b9ff73016bf598ae07515f97289ce8074e97a5/src/Mapping/PropertyAccessors/TypedNoDefaultPropertyAccessor.php#L12-L48","documentation":"The second invariant of TypedNoDefaultPropertyAccessor: since its setValue(null) path unsets the property to represent 'uninitialized', a nullable type would make null and uninitialized indistinguishable and break the strategy. The constructor therefore throws InvalidArgumentException when the property type allows null, even if it is typed.","triggerScenarios":"Directly constructing TypedNoDefaultPropertyAccessor for a property declared nullable (e.g. `public ?string $name;` or `public string|null $name;`) — typed but allowing null.","commonSituations":"Custom accessor pipelines wrapping nullable fields; widening a property's type to nullable later while a custom wrapper still targets it; copied factory logic without the allowsNull() branch.","solutions":["Exclude nullable properties from this accessor; wrap only `public string $x;`-style declarations.","Use PropertyAccessorFactory::createPropertyAccessor() — it already checks allowsNull() before wrapping.","If the property must be nullable, remove the TypedNoDefaultPropertyAccessor wrapper and handle null writes with the parent accessor."],"exampleFix":"// before\n$accessor = new TypedNoDefaultPropertyAccessor($inner, $refl); // ?string $name -> InvalidArgumentException\n\n// after\n$accessor = (! $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":["Only wrap non-nullable typed properties with TypedNoDefaultPropertyAccessor.","Do not widen entity property types to nullable without adjusting accessor wiring.","Prefer PropertyAccessorFactory, which checks allowsNull() for you."],"tags":["doctrine-orm","property-accessor","nullable","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"}