{"record":{"id":"342d4318dc92d448","repo":"withastro/astro","slug":"parameter-name-must-match-a-za-z0-9","errorCode":null,"errorMessage":"Parameter name must match /^[a-zA-Z0-9_$]+$/","messagePattern":"Parameter name must match /\\^\\[a-zA-Z0-9_\\$\\]\\+\\$/","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/integrations/cloudflare/src/utils/generate-routes-json.ts","lineNumber":18,"sourceCode":"import type { RoutePart } from 'astro';\n\n// QUESTION could be removed when we expose it from core https://discord.com/channels/830184174198718474/1471406261294727254\n// Copied from https://github.com/withastro/astro/blob/3776ecf0aa9e08a992d3ae76e90682fd04093721/packages/astro/src/core/routing/manifest/create.ts#L45-L70\n// We're not sure how to improve this regex yet\n// eslint-disable-next-line regexp/no-super-linear-backtracking\nconst ROUTE_DYNAMIC_SPLIT = /\\[(.+?\\(.+?\\)|.+?)\\]/;\nconst ROUTE_SPREAD = /^\\.{3}.+$/;\nexport function getParts(part: 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('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","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/integrations/cloudflare/src/utils/generate-routes-json.ts#L1-L30","documentation":"`getParts` parses a single path segment of an Astro file-based route into dynamic/static parts. After splitting on `[...]`, the content captured inside a dynamic bracket must satisfy `/^(?:\\.\\.\\.)?[\\w$]+$/` — an optional leading spread (`...`) followed by word characters and `$`. Anything else (spaces, hyphens, unicode, dots other than the spread, regex like-parameters with invalid names) throws a plain `Error`.","triggerScenarios":"Calling `getParts(part)` (indirectly during `_routes.json` generation for the Cloudflare adapter) with a route segment whose bracketed name is malformed, e.g. `src/pages/[my-param].astro`, `src/pages/[foo.bar].astro`, `src/pages/[1num].astro`, or a regex param `[...]` whose captured name fails the word-char test.","commonSituations":"Naming a dynamic route param with a hyphen (`[user-id]`) or dot. Copying a route filename from another framework. Mixing rest-spread syntax incorrectly (`[...slug.extra]`). Building on Cloudflare triggers `generate-routes-json` which invokes this parser.","solutions":["Rename the dynamic segment file so the bracketed name matches `/^[a-zA-Z0-9_$]+$/` (e.g. `[user-id]` → `[userId]`).","For spread/rest params use exactly `[...name]` with a valid identifier name.","For regex params, ensure the leading name portion is a valid identifier (e.g. `[param]` not `[-param]`).","Run `astro sync` / `astro build` after renaming to confirm `_routes.json` generation succeeds."],"exampleFix":"// before\nsrc/pages/blog/[post-id].astro\n\n// after\nsrc/pages/blog/[postId].astro","handlingStrategy":"validation","validationCode":"const DYNAMIC_NAME = /^(?:\\.\\.\\.)?[a-zA-Z0-9_$]+$/;\nfunction isValidRouteName(segment: string): boolean {\n  const match = segment.match(/\\[([^\\]]+)\\]/);\n  return !match || DYNAMIC_NAME.test(match[1]);\n}\n// before build:\nif (!isValidRouteName('[user-id]')) throw new Error('bad route name');","typeGuard":"function isValidDynamicParam(content: string): boolean {\n  return /^(?:\\.\\.\\.)?[a-zA-Z0-9_$]+$/.test(content);\n}","tryCatchPattern":"try {\n  getParts(segment);\n} catch (e) {\n  throw new Error(`Invalid route segment \"${segment}\": dynamic param names must match /^[a-zA-Z0-9_$]+$/`);\n}","preventionTips":["Use only letters, digits, underscore, and `$` in dynamic route filenames.","Prefer camelCase for multi-word params.","Lint route filenames in CI before build."],"tags":["routing","cloudflare","file-naming","build"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}