{"record":{"id":"b74e1ec4e59deb0b","repo":"withastro/astro","slug":"invalid-route-file-brackets-are-unbalanced","errorCode":null,"errorMessage":"Invalid route ${file} — brackets are unbalanced","messagePattern":"Invalid route (.+?) — brackets are unbalanced","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/core/routing/segment.ts","lineNumber":8,"sourceCode":"export function validateSegment(segment: string, file = '') {\n\tif (!file) file = segment;\n\n\tif (segment.includes('][')) {\n\t\tthrow new Error(`Invalid route ${file} \\u2014 parameters must be separated`);\n\t}\n\tif (countOccurrences('[', segment) !== countOccurrences(']', segment)) {\n\t\tthrow new Error(`Invalid route ${file} \\u2014 brackets are unbalanced`);\n\t}\n\tif (\n\t\t(/.+\\[\\.\\.\\.[^\\]]+\\]/.test(segment) || /\\[\\.\\.\\.[^\\]]+\\].+/.test(segment)) &&\n\t\tfile.endsWith('.astro')\n\t) {\n\t\tthrow new Error(`Invalid route ${file} \\u2014 rest parameter must be a standalone segment`);\n\t}\n}\n\nfunction countOccurrences(needle: string, haystack: string) {\n\tlet count = 0;\n\tfor (const hay of haystack) {\n\t\tif (hay === needle) count += 1;\n\t}\n\treturn count;\n}\n","sourceCodeStart":1,"sourceCodeEnd":25,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/core/routing/segment.ts#L1-L25","documentation":"Astro validates the bracket structure of every route segment at route-collection time (build and dev start). This error fires from validateSegment when a dynamic route filename contains an unequal count of '[' and ']', so a parameter name cannot be parsed. It is a build-time routing error, not a runtime one.","triggerScenarios":"A file under src/pages/ whose name has unmatched brackets, e.g. src/pages/blog/[id.astro (missing ']'), src/pages/[slug]].astro (extra ']'), or src/pages/post[id.astro. validateSegment is invoked per segment during route discovery.","commonSituations":"Renaming a dynamic route file and forgetting the closing bracket; copy-pasting a [param] token and dropping one bracket; accidentally using literal '[' or ']' in a static filename (Astro treats them as parameter delimiters).","solutions":["Rename the file so every '[' has a matching ']' wrapping a single parameter, e.g. [id].astro.","Search the reported filename for stray brackets and remove any literal '[' or ']'.","Run `astro dev` again to confirm validateSegment no longer throws."],"exampleFix":"// before\nsrc/pages/blog/[id.astro\n// after\nsrc/pages/blog/[id].astro","handlingStrategy":"validation","validationCode":"function hasBalancedBrackets(name) {\n  let depth = 0;\n  for (const ch of name) {\n    if (ch === '[') depth++;\n    else if (ch === ']') depth--;\n    if (depth < 0) return false;\n  }\n  return depth === 0;\n}\nfor (const f of routeFiles) {\n  if (!hasBalancedBrackets(f)) throw new Error(`Unbalanced brackets in ${f}`);\n}","typeGuard":"const isValidSegmentName = (name: string): boolean =>\n  (name.match(/\\[/g)?.length ?? 0) === (name.match(/\\]/g)?.length ?? 0) && !name.includes('][');","tryCatchPattern":null,"preventionTips":["Use an editor that highlights matched brackets when naming route files.","Adopt the [param] convention exactly once per parameter.","Lint src/pages filenames in a prebuild script."],"tags":["routing","build","filesystem"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}