withastro/astro · error · Error
Invalid route ${file} — brackets are unbalanced
Error message
Invalid route ${file} — brackets are unbalanced What it means
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.
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 d081033d5f)
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.
Example fix
// before src/pages/blog/[id.astro // after src/pages/blog/[id].astro
Defensive patterns
Strategy: validation
Validate before calling
function hasBalancedBrackets(name) {
let depth = 0;
for (const ch of name) {
if (ch === '[') depth++;
else if (ch === ']') depth--;
if (depth < 0) return false;
}
return depth === 0;
}
for (const f of routeFiles) {
if (!hasBalancedBrackets(f)) throw new Error(`Unbalanced brackets in ${f}`);
} Type guard
const isValidSegmentName = (name: string): boolean =>
(name.match(/\[/g)?.length ?? 0) === (name.match(/\]/g)?.length ?? 0) && !name.includes(']['); Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- [RSS] You can only glob entries within 'src/pages/' when pas
- CannotDetermineWeightAndStyleFromFontFile
- UnknownFilesystemError
- UnknownFilesystemError
- UnknownFilesystemError
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/b74e1ec4e59deb0b.
Report an issue: GitHub.