symfony/http-kernel · error · LogicException

Validation groups must be strings.

Error message

Validation groups must be strings.

What it means

After excluding Expression, Closure, and GroupSequence elements, every remaining element of a validation-groups array must be a plain string. Any other scalar/object element causes this LogicException.

Solutions

  1. Convert non-string group values to strings (e.g. $enum->value)
  2. Fix the group constants to be string constants
  3. Filter/normalize the groups array before returning it from the closure

Example fix

// before
validationGroups: fn() => [Group::Strict, 'Default']
// after
validationGroups: fn() => [Group::Strict->value, 'Default']
Defensive patterns

Strategy: validation

Validate before calling

foreach ($groups as $g) { if (!is_string($g)) { throw new \InvalidArgumentException('group must be string'); } }

Type guard

is_array($groups) && array_all($groups, 'is_string')

Try / catch

try { $response = $kernel->handle($request); } catch (\LogicException $e) { if (str_contains($e->getMessage(), 'must be strings')) { /* stringify group values */ } throw $e; }

Prevention

When it happens

Trigger: Passing validationGroups: ['Default', 42] or an array containing null/enum objects to a payload attribute's validationGroups.

Common situations: Using integer constants for groups, forgetting to cast backed enums to strings, or building the groups array dynamically with mixed values.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of symfony/http-kernel@aa3a39d728 (2026-09-13). Data as JSON: /api/errors/38a07d579400ec36. Report an issue: GitHub.

Appendix: source

Thrown at Controller/ArgumentResolver/RequestPayloadValueResolver.php:357

        if (!\is_array($validationGroups)) {
            throw new \LogicException('The validation groups expression or closure must return a string, an array of strings, or a GroupSequence.');
        }

        foreach ($validationGroups as $group) {
            if ($group instanceof Expression) {
                throw new \LogicException('Nested expressions in validation groups are not supported. Use a single Expression or a list of strings (or a GroupSequence) instead.');
            }

            if ($group instanceof \Closure) {
                throw new \LogicException('Nested closures in validation groups are not supported. Use a single Closure or a list of strings (or a GroupSequence) instead.');
            }

            if ($group instanceof GroupSequence) {
                throw new \LogicException('GroupSequence cannot be used inside an array of validation groups. Pass the GroupSequence as the top-level validationGroups value instead.');
            }

            if (!\is_string($group)) {
                throw new \LogicException('Validation groups must be strings.');
            }
        }

        return $validationGroups;
    }

    private static function hasNonStringScalar(array $data): bool
    {
        $stack = [$data];

        while ($stack) {
            foreach (array_pop($stack) as $v) {
                if (\is_array($v)) {
                    $stack[] = $v;
                } elseif (!\is_string($v) && !\is_object($v)) {
                    // uploaded files are merged into the payload of a "form" request; being objects,
                    // they are not scalars and must not make the payload look like a typed one
                    return true;

View on GitHub (pinned to aa3a39d728)