Skip to content

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

Open
vincentchalamon wants to merge 1 commit into4.3from
fix/reproducer-serializer-enum-601
Open

test(api): reproducer for DeserializeProvider enum validation bug#617
vincentchalamon wants to merge 1 commit into4.3from
fix/reproducer-serializer-enum-601

Conversation

@vincentchalamon
Copy link
Copy Markdown
Contributor

@vincentchalamon vincentchalamon commented Mar 27, 2026

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
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>
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