{"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":"exception","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/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/astro/src/core/routing/segment.ts#L1-L25","documentation":"Dynamic-route params must be wrapped in matched [...] pairs. validateSegment counts opening and closing brackets with countOccurrences and throws when they differ, e.g. [id.astro (missing ]) or id]].astro (extra ]). An unbalanced pattern cannot be compiled into a route regex, and leaving the mistake in place would make every URL match (or fail to match) unpredictably.","triggerScenarios":"Any pages-directory filename or segment where the number of '[' characters is not equal to the number of ']' characters: src/pages/[slug.astro, src/pages/blog]].astro, src/pages/[...tags.astro.","commonSituations":"Typo while creating a dynamic route; editor auto-rename cutting a bracket; copying a route filename from docs and losing a character; shell escaping eating a bracket when creating files via CLI.","solutions":["Find the offending file (the error message names it in ${file}) and rename it so every '[' has a matching ']'.","Double-check rest params specifically: [...slug].astro needs both the inner dots and both brackets."],"exampleFix":"// before\nsrc/pages/blog/[slug.astro      // 1 '[' vs 0 ']' -> unbalanced\n\n// after\nsrc/pages/blog/[slug].astro      // brackets balanced","handlingStrategy":"validation","validationCode":"function isBalanced(segment: string): boolean {\n  const open = [...segment].filter((c) => c === '[').length;\n  const close = [...segment].filter((c) => c === ']').length;\n  return open === close;\n}\n\n// before running astro dev/build, assert every pages/ filename passes isBalanced","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Create dynamic route files by copy-pasting a known-good [slug].astro and only editing inside the brackets.","Quote filenames in shell commands so brackets are not glob-expanded.","Let your editor's filename validation or a CI filename check flag stray brackets."],"tags":["astro","routing","file-naming","dynamic-routes","build-time"],"backgroundTag":"route-parameter-validation-failed","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"}