withastro/astro · error · Error
Invalid route — brackets are unbalanced
Error message
Invalid route ${file} — brackets are unbalanced What it means
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.
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.
Example fix
// before src/pages/blog/[slug.astro // 1 '[' vs 0 ']' -> unbalanced // after src/pages/blog/[slug].astro // brackets balanced
Defensive patterns
Strategy: validation
Validate before calling
function isBalanced(segment: string): boolean {
const open = [...segment].filter((c) => c === '[').length;
const close = [...segment].filter((c) => c === ']').length;
return open === close;
}
// before running astro dev/build, assert every pages/ filename passes isBalanced Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid route — parameters must be separated
- Invalid route — rest parameter must be a standalone segment
- Invalid route — parameter name must match /^[a-zA-Z0-9_$]+$/
- getStaticPaths() ignored in dynamic page
- GetStaticPathsRequired
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/b74e1ec4e59deb0b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/core/routing/segment.ts:8
export function validateSegment(segment: string, file = '') {
if (!file) file = segment;
if (segment.includes('][')) {
throw new Error(`Invalid route ${file} \u2014 parameters must be separated`);
}
if (countOccurrences('[', segment) !== countOccurrences(']', segment)) {
throw new Error(`Invalid route ${file} \u2014 brackets are unbalanced`);
}
if (
(/.+\[\.\.\.[^\]]+\]/.test(segment) || /\[\.\.\.[^\]]+\].+/.test(segment)) &&
file.endsWith('.astro')
) {
throw new Error(`Invalid route ${file} \u2014 rest parameter must be a standalone segment`);
}
}
function countOccurrences(needle: string, haystack: string) {
let count = 0;
for (const hay of haystack) {
if (hay === needle) count += 1;
}
return count;
}
View on GitHub (pinned to 52e6c34790)