Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Validation groups are skipped when deserializing invalid PHP enums (BackedEnumNormalizer throws before validation) #2968

@BriceFab

Description

@BriceFab

API Platform version(s) affected

3.4.x (likely affects all versions using Symfony BackedEnumNormalizer)

Description

When using PHP Backed Enums as property values inside an ApiResource, invalid enum values sent by a client never reach Symfony Validator.

Instead, the request fails during deserialization, and API Platform returns a generic 400 error:

The data must belong to a backed enumeration of type App\BusinessLogic\Constants\GoalType

This happens before validation, which means:

  • Validation constraints (Assert\Choice, Assert\NotNull, etc.) are never executed
  • Validation groups (validationContext) are ignored
  • The error cannot be handled consistently through API Platform’s ConstraintViolationList (422)

This is problematic for public APIs because enum validation becomes inconsistent and impossible to centralize through validation groups.

How to reproduce

Entity:

#[ORM\Entity]
class FitnessProfile
{
    #[ORM\Column(nullable: true, enumType: GoalType::class)]
    #[Assert\Choice(
        callback: [GoalType::class, 'values'],
        groups: ['api:create', 'api:update'],
    )]
    private ?GoalType $goal = null;
}
namespace App\BusinessLogic\Constants;

enum GoalType: string
{
    case LOSE_WEIGHT        = 'fat_loss';
    case BUILD_MUSCLE       = 'muscle_gain';
    case GET_STRONGER       = 'get_stronger';
    case ENDURANCE          = 'endurance';
    case MAINTENANCE        = 'maintenance';
    case PERFORMANCE = 'performance';
}

Resource:

#[ApiResource(
    operations: [
        new Post(
            uriTemplate: '/lead',
            denormalizationContext: ['groups' => ['api:create']],
            validationContext: ['groups' => 'api:create'],
        )
    ]
)]
class Lead
{
    #[Assert\Valid(groups: ['api:create', 'api:update'])]
    private ?FitnessProfile $fitnessProfile = null;
}

Request:

{
  "fitnessProfile": {
    "goal": "muscle2_gain"
  }
}

Actual Result

API Platform fails during deserialization:

400 Bad Request
{
  "title": "An error occurred",
  "detail": "The data must belong to a backed enumeration of type App\\BusinessLogic\\Constants\\GoalType"
}

Stack trace shows failure in:

Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer::denormalize()

Validator is never executed.


Expected Result

Validation should run and produce a 422 with a constraint violation, respecting validation groups:

{
  "violations": [
    {
      "propertyPath": "fitnessProfile.goal",
      "message": "This value is not valid."
    }
  ]
}

In other words:

Enum deserialization errors should behave like normal validation errors, not bypass validation entirely.


Possible Solutions

1️⃣ Delay enum coercion until after validation

Let validation handle “invalid enum value” the same way Choice currently does.

2️⃣ Convert enum denormalization errors into ConstraintViolationList

Instead of throwing immediately, convert NotNormalizableValueException into a violation.

3️⃣ Provide documented official pattern

For example, recommended use of:

  • Input DTOs with string + validation
  • or Serializer option (ALLOW_INVALID_VALUES) enabled by default in API Platform contexts

…but today none of these approaches are officially supported or explained.


Additional Context

Thanks for your work — enums as resources are great, but validation consistency would make them far more usable in real-world APIs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions