withastro/astro · error · Error

Invalid route ${file} — parameter name must match /^[a-zA-Z0

Error message

Invalid route ${file} — parameter name must match /^[a-zA-Z0-9_$]+$/

What it means

Thrown by Vercel's `getParts()` route parser when a dynamic route segment's parameter name does not match `/^[a-zA-Z0-9_$]+$/`. Vercel's redirect/route transform needs valid JS-identifier-like names for path parameters; invalid characters make the route unrepresentable.

Source

Thrown at packages/integrations/vercel/src/lib/redirects.ts:22

import type { AstroConfig, IntegrationResolvedRoute, RoutePart } from 'astro';

const pathJoin = nodePath.posix.join;

// Copied from astro/packages/astro/src/core/routing/manifest/create.ts
// Disable eslint as we're not sure how to improve this regex yet
// eslint-disable-next-line regexp/no-super-linear-backtracking
const ROUTE_DYNAMIC_SPLIT = /\[(.+?\(.+?\)|.+?)\]/;
const ROUTE_SPREAD = /^\.{3}.+$/;
function getParts(part: string, file: string) {
	const result: RoutePart[] = [];
	part.split(ROUTE_DYNAMIC_SPLIT).map((str, i) => {
		if (!str) return;
		const dynamic = i % 2 === 1;

		const [, content] = dynamic ? /([^(]+)$/.exec(str) || [null, null] : [null, str];

		if (!content || (dynamic && !/^(?:\.\.\.)?[\w$]+$/.test(content))) {
			throw new Error(`Invalid route ${file} — parameter name must match /^[a-zA-Z0-9_$]+$/`);
		}

		result.push({
			content,
			dynamic,
			spread: dynamic && ROUTE_SPREAD.test(content),
		});
	});

	return result;
}
/**
 * Convert Astro routes into Vercel path-to-regexp syntax, which are the input for getTransformedRoutes
 */
function getMatchPattern(segments: RoutePart[][]) {
	return segments
		.map((segment) => {
			return segment

View on GitHub (pinned to d081033d5f)

Solutions

  1. Rename the param to use only letters, digits, underscore, or `$`: `[userId].astro` or `[user_id].astro`.
  2. For rest/spread params, keep `[...paramName]` with a valid identifier.
  3. Avoid hyphens and dots in the bracketed param name; use camelCase or underscores instead.

Example fix

// before: src/pages/blog/[post-slug].astro
// after: src/pages/blog/[postSlug].astro
Defensive patterns

Strategy: validation

Validate before calling

function isValidParamName(name: string): boolean {
  return /^[a-zA-Z0-9_$]+$/.test(name);
}
// before shipping routes, scan filenames:
for (const f of pageFiles) {
  for (const [, param] of f.matchAll(/\[(.+?)\]/g)) {
    if (!isValidParamName(param)) throw new Error(`Invalid param '${param}' in ${f}`);
  }
}

Type guard

function isValidRouteParam(name: string): boolean {
  return /^(?:\.\.\.)?[a-zA-Z0-9_$]+$/.test(name);
}

Prevention

When it happens

Trigger: A file/folder like `src/pages/[user-id].astro` (hyphen), `[user.name].astro` (dot), `[user@name].astro`, or a rest spread `[...paths with space]`. Any dynamic param whose content after stripping optional regex group syntax fails `/^(?:\.\.\.)?[\w$]+$/`.

Common situations: Naming route params with hyphens (common mistake from URL conventions). Using dots or special chars. Copying a URL slug pattern directly into a filename.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/c15bf8c363cf7b90. Report an issue: GitHub.