{"record":{"id":"dd39a050f2565d27","repo":"withastro/astro","slug":"invalid-route-file-parameter-name-must-match","errorCode":null,"errorMessage":"Invalid route ${file} — parameter name must match /^[a-zA-Z0-9_$]+$/","messagePattern":"Invalid route (.+?) — parameter name must match /\\^\\[a-zA-Z0-9_\\$\\]\\+\\$/","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/core/routing/create-manifest.ts","lineNumber":61,"sourceCode":"\nconst ROUTE_DYNAMIC_SPLIT = /\\[([^[\\]()]+(?:\\([^)]+\\))?)\\]/;\nconst ROUTE_SPREAD = /^\\.{3}.+$/;\n\nexport interface RouteEntry {\n\tpath: string;\n\tisDir: boolean;\n}\n\nfunction getParts(part: string, file: string) {\n\tconst result: RoutePart[] = [];\n\tpart.split(ROUTE_DYNAMIC_SPLIT).map((str, i) => {\n\t\tif (!str) return;\n\t\tconst dynamic = i % 2 === 1;\n\n\t\tconst [, content] = dynamic ? /([^(]+)$/.exec(str) || [null, null] : [null, str];\n\n\t\tif (!content || (dynamic && !/^(?:\\.\\.\\.)?[\\w$]+$/.test(content))) {\n\t\t\tthrow new Error(`Invalid route ${file} — parameter name must match /^[a-zA-Z0-9_$]+$/`);\n\t\t}\n\n\t\tresult.push({\n\t\t\tcontent,\n\t\t\tdynamic,\n\t\t\tspread: dynamic && ROUTE_SPREAD.test(content),\n\t\t});\n\t});\n\n\treturn result;\n}\n/**\n * Checks whether two route segments are semantically equivalent.\n *\n * Two segments are equivalent if they would match the same paths. This happens when:\n * - They have the same length.\n * - Each part in the same position is either:\n *   - Both static and with the same content (e.g. `/foo` and `/foo`).","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/core/routing/create-manifest.ts#L43-L79","documentation":"While creating the route manifest, Astro splits each file path segment on `[...]` groups; any dynamic part's content must match `/^(?:\\.{3})?[\\w$]+$/` — optionally a `...` spread prefix, then only letters, digits, underscore, and dollar sign. Anything else (hyphens, dots, spaces, unicode) makes the param impossible to treat as a valid identifier, so manifest creation throws this plain Error naming the offending file.","triggerScenarios":"A page or endpoint named `src/pages/[my-slug].astro` (hyphen), `src/pages/[slug.json].ts` (dot inside brackets), `src/pages/[...path-segment].astro`, or `src/pages/[categoría].astro` (unicode); renaming a static folder into bracket notation with invalid characters; generating pages programmatically with unvalidated names.","commonSituations":"Reaching for kebab-case param names to match URL style; converting a flat route (`/my-slug`) to dynamic and keeping the hyphen; scaffolding scripts that inject arbitrary strings into filenames.","solutions":["Rename the param to use only `[A-Za-z0-9_$]`, e.g. `[mySlug].astro` — the param value can still contain hyphens at runtime, only the name is restricted","Update `getStaticPaths()`/`Astro.params` keys to the renamed param","Scan `src/pages` for bracket segments failing `/^(?:\\.\\.\\.)?[\\w$]+$/` to catch all offenders at once"],"exampleFix":"# before\nsrc/pages/blog/[my-slug].astro\n\n# after\nsrc/pages/blog/[mySlug].astro\n# and update: getStaticPaths() → [{ params: { mySlug: 'some-value' } }]","handlingStrategy":"type-guard","validationCode":"# CI scan: fail on bracket params with invalid names\nfind src/pages -name '*\\[*\\]*' | while read -r f; do\n  echo \"$f\" | grep -oE '\\[[^]/]+\\]' | tr -d '[]' | while read -r p; do\n    p=\"${p#...}\"; is_valid_param_name \"$p\" || { echo \"invalid param '$p' in $f\"; exit 1; }\n  done\ndone","typeGuard":"// Mirrors Astro's rule: optional '...' spread, then word chars / $ only\nexport function isValidRouteParamName(name: string): boolean {\n  return /^(?:\\.\\.\\.)?[\\w$]+$/.test(name);\n}","tryCatchPattern":null,"preventionTips":["Use camelCase for dynamic segment names; keep hyphens for URL values, not param names","Add the filename scan to CI so scaffolders and renames fail loudly","Never interpolate user/CMS strings directly into file-based route filenames"],"tags":["file-based-routing","routing","build","naming"],"backgroundTag":"invalid-route-parameter-name","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}