{"record":{"id":"18e53e145c24c760","repo":"symfony/routing","slug":"a-placeholder-name-must-be-a-string-d-given-did-you-forget","errorCode":null,"errorMessage":"A placeholder name must be a string (%d given). Did you forget to specify the placeholder key for the requirement \"%s\" of route \"%s\" in \"%s::%s()\"?","messagePattern":"A placeholder name must be a string \\((.+?) given\\)\\. Did you forget to specify the placeholder key for the requirement \"(.+?)\" of route \"(.+?)\" in \"(.+?)::(.+?)\\(\\)\"\\?","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"Loader/AttributeClassLoader.php","lineNumber":158,"sourceCode":"    }\n\n    /**\n     * @param RouteAttribute $attr or an object that exposes a similar interface\n     */\n    protected function addRoute(RouteCollection $collection, object $attr, array $globals, \\ReflectionClass $class, \\ReflectionMethod $method): void\n    {\n        if ($attr->envs && !\\in_array($this->env, $attr->envs, true)) {\n            return;\n        }\n\n        $name = $attr->name ?? $this->getDefaultRouteName($class, $method);\n        $name = $globals['name'].$name;\n\n        $requirements = $attr->requirements;\n\n        foreach ($requirements as $placeholder => $requirement) {\n            if (\\is_int($placeholder)) {\n                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\" of route \"%s\" in \"%s::%s()\"?', $placeholder, $requirement, $name, $class->getName(), $method->getName()));\n            }\n        }\n\n        $defaults = array_replace($globals['defaults'], $attr->defaults);\n        $requirements = array_replace($globals['requirements'], $requirements);\n        $options = array_replace($globals['options'], $attr->options);\n        $schemes = array_unique(array_merge($globals['schemes'], $attr->schemes));\n        $methods = array_unique(array_merge($globals['methods'], $attr->methods));\n\n        $host = $attr->host ?? $globals['host'];\n        $condition = $attr->condition ?? $globals['condition'];\n        $priority = $attr->priority ?? $globals['priority'];\n\n        $path = $attr->path;\n        $prefix = $globals['localized_paths'] ?: $globals['path'];\n        $paths = [];\n\n        if (\\is_array($path)) {","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/symfony/routing/blob/83fa223250b50f4f018c011e101c330e65ac63cc/Loader/AttributeClassLoader.php#L140-L176","documentation":"AttributeClassLoader::addRoute validates that every route requirement key is a placeholder string. When an attribute requirement is passed as a list (e.g. #[Route(..., requirements: ['\\\\d+'])]) the key is an integer, so the loader throws InvalidArgumentException naming the route, class and method.","triggerScenarios":"Declaring an attribute route with a requirements array that uses implicit integer keys, e.g. requirements: ['/d+'] instead of ['id' => '/d+'], so the requirement value has no placeholder name to match against.","commonSituations":"Migrating from YAML/XML where requirements could be positional; typos like requirements: ['\\d+']; refactoring that dropped the placeholder key; copying a route definition and losing the named key.","solutions":["Give each requirement an explicit string key matching the route placeholder: requirements: ['id' => '\\d+']","Verify every requirements entry has a named key on the #[Route]/#[RouteCollision...] attribute","Check the class-level #[Route] attribute globals too, since requirements are merged with globals"],"exampleFix":"// before\n#[Route('/blog/{id}', requirements: ['\\d+'])]\n// after\n#[Route('/blog/{id}', requirements: ['id' => '\\d+'])]","handlingStrategy":"validation","validationCode":"foreach ($attr->requirements as $k => $v) { if (is_int($k)) throw new \\InvalidArgumentException(\"Requirement for placeholder '$v' must use a named key\"); }","typeGuard":"function hasStringRequirementKeys(array $requirements): bool { foreach ($requirements as $k => $v) { if (!is_string($k)) return false; } return true; }","tryCatchPattern":null,"preventionTips":["Always write requirements as placeholder => pattern maps","Grep codebase for requirements: with unkeyed entries","Add static analysis (e.g. PHPStan rule) flagging integer-keyed requirement arrays"],"tags":["routing","php-attributes","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"83fa223250b50f4f018c011e101c330e65ac63cc","analyzedAt":"2026-09-14T03:19:46.280Z","contentChangedAt":"2026-09-14T03:19:46.280Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}