Skip to content

fix(serializer): report enum backing type in denormalization violations#8389

Merged
soyuka merged 1 commit into
api-platform:4.3from
soyuka:fix/enum-denormalization-message-8388
Jul 11, 2026
Merged

fix(serializer): report enum backing type in denormalization violations#8389
soyuka merged 1 commit into
api-platform:4.3from
soyuka:fix/enum-denormalization-message-8388

Conversation

@soyuka

@soyuka soyuka commented Jul 8, 2026

Copy link
Copy Markdown
Member

Description

Fixes #8388.

When a wrong-typed value is sent for a nullable backed enum property (e.g. ?Status), collectDenormalizationErrors produced the violation message:

{ "propertyPath": "status", "message": "This value should be of type Status|null." }

Status|null is the internal PHP type: it appears nowhere in the OpenAPI schema, is not something a JSON consumer can send, and names none of the accepted values. This is a regression from the TypeInfo migration (#6979); on 4.1.x the same request named int|string + a hint.

Root cause

AbstractItemNormalizer delegates a nullable backed enum to BackedEnumNormalizer, which throws an actionable exception (expectedTypes = ['int','string'], useMessageForUser = true, message naming the enum). The post-loop error block then rewrapped it, replacing expectedTypes with (string) $types (["Status","null"]) and dropping the user-facing hint flag. DeserializeProvider renders implode('|', expectedTypes)Status|null, hint gone.

Fix

Now {"status": true} on a ?Status (string-backed, nullable) property yields:

{
  "propertyPath": "status",
  "message": "This value should be of type string|null.",
  "hint": "The data is neither an integer nor a string, you should pass an integer or a string that can be parsed as an enumeration case of type Status."
}

Tests

Added testWrongTypeForBackedEnumReportsAcceptedScalarTypes to EnumDenormalizationValidationTest (the existing #8183 test file). Existing enum + ValidationTest message assertions stay green.

Only validation message content changes — no HTTP status, propertyPath, constraint-code, or response-shape change; non-breaking, targeted at 4.3.

When a wrong-typed value is sent for a nullable backed enum property (e.g.
`?Status`), collectDenormalizationErrors produced the violation message
"This value should be of type Status|null." — the internal PHP type, which
appears nowhere in the OpenAPI schema and names none of the accepted values.

AbstractItemNormalizer delegates nullable backed enums to BackedEnumNormalizer,
which throws an actionable exception (accepted scalars + hint). The post-loop
block then rewrapped it, replacing its expectedTypes with `(string) $types`
("Status|null") and dropping the user-facing hint flag.

Preserve the hint flag when the failure comes from an object/enum normalizer,
and in DeserializeProvider render a BackedEnum expected type as its JSON-visible
backing scalar ("string"/"int") instead of the enum FQCN. The enum class stays
in expectedTypes so the 400->422 promotion (api-platform#8183) keeps working.

Now `{"status": true}` on a `?Status` property yields
"This value should be of type string|null." plus the enum hint.

Closes api-platform#8388
@soyuka soyuka merged commit b396ff9 into api-platform:4.3 Jul 11, 2026
112 checks passed
@soyuka soyuka mentioned this pull request Jul 12, 2026
1 task
soyuka added a commit that referenced this pull request Jul 12, 2026
…olationFactory

#8389 patched normalizeExpectedTypes() in DeserializeProvider on 4.3; 4.4 moved that logic to DenormalizationViolationFactory. Port the backed-enum backing-type reporting and result dedup so the fix survives the up-merge.
soyuka added a commit that referenced this pull request Jul 12, 2026
… test

The 3-way merge of #8389 correctly dropped the deprecation-expectation
guard (imports + #[IgnoreDeprecations] + version check) that #8287 had
already removed from the sibling test, since 4.4's DenormalizationViolationFactory
prefers getNotNormalizableValueErrors() over the deprecated getErrors()
whenever it exists and so never triggers that deprecation on symfony/serializer
>=8.1. But #8389 also introduced a brand-new test reusing the same
now-unimported VersionParser/IgnoreDeprecations symbols, which the
textual merge could not reconcile: it left the merged file referencing
classes with no matching use statement. Remove the same stale guard
from the new test for the same reason #8287 removed it from the old one.
@soyuka soyuka mentioned this pull request Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant