{"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":"exception","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/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/integrations/cloudflare/src/utils/generate-routes-json.ts#L1-L30","documentation":"The Cloudflare adapter generates `_worker.js`/routes.json by converting Astro's file-based routes into URL patterns. Every dynamic segment between brackets must be a plain identifier (`[name]`) or a spread (`[...slug]`); the generator strips any regex suffix and then requires the remainder to match `^(?:\\.\\.\\.)?[\\w$]+$`. Anything else — regex route params, special characters, invalid identifiers — throws this validation error.","triggerScenarios":"A page file like `src/pages/[id(\\d+)].astro` or `src/pages/[user-id!].astro` (content with characters outside `[a-zA-Z0-9_$]` after spread/regex stripping) while the cloudflare adapter generates its routes manifest during build/dev.","commonSituations":"Porting projects that relied on regex route parameters (removed from Astro core) into a Cloudflare-deployed app; typos or URL-unsafe characters inside bracket segments; copying Next.js-style route filenames.","solutions":["Rename the segment to a plain identifier: `[id(\\d+)].astro` → `[id].astro`.","Use `[...slug].astro` only for catch-all/rest segments.","Move format constraints (numeric-only, slug patterns) into the page's `getStaticPaths`/param validation or middleware instead of the filename.","Run a quick scan of src/pages for brackets containing anything beyond letters, digits, underscore, dollar, or a leading `...`."],"exampleFix":"// before — src/pages/[id(\\d+)].astro\n// after  — src/pages/[id].astro (validate inside getStaticPaths)\nexport async function getStaticPaths() {\n  return [1, 2, 3].map((id) => ({ params: { id: String(id) } }));\n}","handlingStrategy":"validation","validationCode":"// Fail fast in CI before the adapter does: scan src/pages for invalid dynamic segments\nimport { globSync } from 'glob';\nimport path from 'node:path';\nconst valid = /^(?:\\.\\.\\.)?[A-Za-z0-9_$]+$/;\nfor (const f of globSync('src/pages/**/*.{astro,ts,js,md}')) {\n  for (const seg of path.parse(f).dir.split(path.sep).concat(path.basename(f, path.extname(f)))) {\n    const m = seg.match(/^\\[(.+)\\]$/);\n    if (m && !valid.test(m[1].replace(/\\([^)]*\\)$/g, ''))) {\n      throw new Error(`Invalid route parameter \"${m[1]}\" in ${f}`);\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep bracketed route segments to plain identifiers and `[...spread]` only.","Put format constraints in getStaticPaths or middleware, never in filenames.","Add the filename lint to CI so bad segments fail before the Cloudflare build."],"tags":["cloudflare","routing","dynamic-routes","build","validation"],"backgroundTag":"invalid-route-parameter","analyzedSha":"52e6c34790cc8ac4e69e6135ace06049867e5c4a","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}