symfony/routing · error · InvalidArgumentException

A placeholder name must be a string

Error message

A placeholder name must be a string (%d given). Did you forget to specify the placeholder key for the requirement "%s" in "%s"?

What it means

getGlobals validates requirement keys coming from a class-level #[Route] attribute before merging them into globals. Integer (positional) keys mean the requirement was given without a placeholder name, so it throws InvalidArgumentException naming the class.

Solutions

  1. Add explicit placeholder keys to the class-level #[Route] requirements
  2. Check each #[Route] attribute on the controller class for named requirement keys
  3. Remember requirements are merged with method-level ones; both sides need string keys

Example fix

// before
#[Route('/blog', requirements: ['\d+'])]
// after
#[Route('/blog', requirements: ['page' => '\d+'])]
Defensive patterns

Strategy: validation

Validate before calling

foreach ($classAttr->requirements as $k => $v) { if (is_int($k)) throw new \InvalidArgumentException('Class-level requirement needs named key'); }

Type guard

function allRequirementsNamed(array $requirements): bool { return arrayevery(fn($k) => is_string($k), array_keys($requirements)); }

Try / catch

try { $routes = $loader->load($file); } catch (\InvalidArgumentException $e) { if (str_contains($e->getMessage(), 'placeholder name must be a string')) { /* fix class attribute */ } throw $e; }

Prevention

When it happens

Trigger: A controller class annotated #[Route(path: '/blog', requirements: ['\d+'])] with a positional requirement, triggering validation of class-level globals during load().

Common situations: Missing named key on class-level requirements; assuming class-level requirements may be positional; copy-paste of a requirement array without its keys.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of symfony/routing@83fa223250 (2026-09-14). Data as JSON: /api/errors/7220c98239d48d70. Report an issue: GitHub.

Appendix: source

Thrown at Loader/AttributeClassLoader.php:330

            if (null !== $attr->methods) {
                $globals['methods'] = $attr->methods;
            }

            if (null !== $attr->host) {
                $globals['host'] = $attr->host;
            }

            if (null !== $attr->condition) {
                $globals['condition'] = $attr->condition;
            }

            $globals['priority'] = $attr->priority ?? 0;
            $globals['env'] = $attr->envs;

            foreach ($globals['requirements'] as $placeholder => $requirement) {
                if (\is_int($placeholder)) {
                    throw new \InvalidArgumentException(\sprintf('A placeholder name must be a string (%d given). Did you forget to specify the placeholder key for the requirement "%s" in "%s"?', $placeholder, $requirement, $class->getName()));
                }
            }
        }

        return $globals;
    }

    private function resetGlobals(): array
    {
        return [
            'path' => null,
            'localized_paths' => [],
            'requirements' => [],
            'options' => [],
            'defaults' => [],
            'schemes' => [],
            'methods' => [],
            'host' => '',

View on GitHub (pinned to 83fa223250)