Skip to content

test(api): reproducer for DeserializeProvider enum validation bug#617

Closed
vincentchalamon wants to merge 2 commits into
4.3from
fix/reproducer-serializer-enum-601
Closed

test(api): reproducer for DeserializeProvider enum validation bug#617
vincentchalamon wants to merge 2 commits into
4.3from
fix/reproducer-serializer-enum-601

Conversation

@vincentchalamon

@vincentchalamon vincentchalamon commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Status

Waiting for api-platform/core#7894

Summary

Reproducer for #601 — API Platform's DeserializeProvider uses the generic Type constraint message ("This value should be of type X.") even when the serializer exception provides a user-friendly message. This results in:

  • Symfony 8.0: unhelpful "This value should be of type int|string." instead of the enum-specific message
  • Symfony 8.1 (upcoming): broken "This value should be of type ." (empty type) when expectedTypes is null

Context

Symfony PR #62574 (commit 35b1aec) improves BackedEnumNormalizer error messages. This change was:

  1. Merged to 8.1 branch (Dec 2025)
  2. Accidentally cherry-picked into 8.0.5 — which broke this demo (see PR #587 comment)
  3. Reworked in 8.0.6 (commit 388c311) — the expectedTypes = null behavior was removed from 8.0.x
  4. The demo jumped from v8.0.4 → v8.0.7, skipping the broken v8.0.5

On the current Symfony 8.1 branch, BackedEnumNormalizer distinguishes:

  • Type mismatch (TypeError): expectedTypes = [$backingType]
  • Invalid value (ValueError): expectedTypes = ['int', 'string'], message = "The data must belong to a backed enumeration of type $type"

Where the bug is

In api-platform/state v4.3.3DeserializeProvider.php:

$message = (new Type($expectedTypes))->message;
// Always uses "This value should be of type {{ type }}."
// even when the exception has a better, user-friendly message
$violations->add(new ConstraintViolation(
    $this->translator->trans($message, ['{{ type }}' => implode('|', $expectedTypes)], 'validators'),
    ...
));

The DeserializeProvider ignores canUseMessageForUser() for the violation message — it only uses the exception message as a hint parameter, not as the main violation message.

What this PR does

  • Adds a BackedEnumNormalizerDecorator that simulates the Symfony 8.1 behavior (two distinct error paths with expectedTypes = null for invalid values)
  • Updates test expectations in BookTest::getInvalidData() to verify the expected correct behavior for both Symfony versions:
    • With decorator (Symfony 8.1): expects the list of valid enum values
    • Without decorator (Symfony 8.0): expects the enum FQCN message

Proposed fix (upstream in api-platform/core)

When canUseMessageForUser() is true, use the exception message directly as the violation message:

$parameters = [];
if ($exception->canUseMessageForUser()) {
    $parameters['hint'] = $exception->getMessage();
    $violationMessage = $exception->getMessage();
    $violations->add(new ConstraintViolation($violationMessage, $violationMessage, $parameters, null, $exception->getPath(), null, null, (string) Type::INVALID_TYPE_ERROR));
} else {
    $message = (new Type($expectedTypes))->message;
    $violations->add(new ConstraintViolation($this->translator->trans($message, ['{{ type }}' => implode('|', $expectedTypes)], 'validators'), $message, $parameters, null, $exception->getPath(), null, null, (string) Type::INVALID_TYPE_ERROR));
}

A git patch is included in this PR: 0001-fix-DeserializeProvider-use-exception-message-when-available.patch

Tested locally — results

Scenario Decorator Patch Result
Symfony 8.0 current No No "This value should be of type int|string." — unhelpful
Symfony 8.1 simulated Yes No "This value should be of type ."broken
Symfony 8.0 + patch No Yes "The data must belong to a backed enumeration of type App\Enum\BookCondition"7/7 tests pass
Symfony 8.1 + patch Yes Yes "The data must be one of the following values: '...', ..."7/7 tests pass

Test plan

  • CI will show failing tests for the empty data and invalid condition cases — this is expected and demonstrates the bug in DeserializeProvider
  • Applying the upstream patch makes all tests pass with both Symfony 8.0 and 8.1 behavior
  • Once the fix lands upstream in api-platform/core, the decorator can be removed and tests will pass natively

Closes #601

🤖 Generated with Claude Code

@vincentchalamon vincentchalamon linked an issue Mar 27, 2026 that may be closed by this pull request
@vincentchalamon vincentchalamon changed the base branch from 4.2 to 4.3 March 29, 2026 18:26
@vincentchalamon vincentchalamon force-pushed the fix/reproducer-serializer-enum-601 branch from 7f4b450 to ba9ce88 Compare March 29, 2026 18:38
@vincentchalamon vincentchalamon self-assigned this Apr 1, 2026
Reproduces the bug that will occur when upgrading to Symfony 8.1.
Symfony PR #62574 (commit 35b1aec) improves BackedEnumNormalizer error
messages, but API Platform's DeserializeProvider does not handle the
case where expectedTypes is null/empty, producing:
"This value should be of type ." (empty type).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vincentchalamon vincentchalamon force-pushed the fix/reproducer-serializer-enum-601 branch from ba9ce88 to 3a4ff69 Compare April 30, 2026 14:13
@vincentchalamon

Copy link
Copy Markdown
Contributor Author

Fix merged on API Platform.

@vincentchalamon vincentchalamon deleted the fix/reproducer-serializer-enum-601 branch April 30, 2026 14:23
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.

Investigate with symfony/serializer:8.0.5

1 participant