withastro/astro · warning

The route "${a.route}" is defined in both "${a.component}" a

Error message

The route "${a.route}" is defined in both "${a.component}" and "${b.component}" using SSR mode. A dynamic SSR route cannot be defined more than once.

What it means

For server-rendered routes, validateCollisions compares dynamic patterns segment-by-segment with semantic equality: [id] and [slug] at the same position are equivalent because both match any single value. When two files yield segment-equal dynamic routes in SSR mode, only one can match, so Astro warns (domain 'router') that a dynamic SSR route cannot be defined more than once.

Source

Thrown at packages/astro/src/core/routing/create-manifest.ts:685

		return;
	}

	// Routes have the same number of segments, can use either.
	const segmentCount = a.segments.length;

	for (let index = 0; index < segmentCount; index++) {
		const segmentA = a.segments[index];
		const segmentB = b.segments[index];

		if (!isSemanticallyEqualSegment(segmentA, segmentB)) {
			// If any segment is not semantically equal between the routes
			// it is not certain that the routes collide.
			return;
		}
	}

	// Both routes are guaranteed to collide such that one will never be matched.
	logger.warn(
		'router',
		`The route "${a.route}" is defined in both "${a.component}" and "${b.component}" using SSR mode. A dynamic SSR route cannot be defined more than once.`,
	);
	logger.warn('router', 'A collision will result in a hard error in following versions of Astro.');
}

/**
 * Create a full route manifest from filesystem and injected routes.
 */
export async function createRoutesList(
	params: CreateRouteManifestParams,
	logger: AstroLogger,
	{
		dev = false,
	}: {
		dev?: boolean;
	} = {},
): Promise<RoutesList> {

View on GitHub (pinned to e294953aa8)

Solutions

  1. Keep exactly one file per dynamic pattern — delete or rename the duplicate
  2. Differentiate the routes with distinct static segments (e.g. /blog/[slug] vs /news/[id])
  3. If both behaviors are needed, merge into one route that branches internally on the parameter value

Example fix

# before — semantically identical in SSR
src/pages/shop/[slug].astro
src/pages/shop/[id].astro

# after
src/pages/shop/[slug].astro  # keep one; merge any extra logic into it
Defensive patterns

Strategy: validation

Validate before calling

import { readdirSync } from 'node:fs';
import { join } from 'node:path';
function normalize(dir, prefix = '') {
  const out = [];
  for (const e of readdirSync(dir, { withFileTypes: true })) {
    const seg = e.name.replace(/\.(astro|js|ts)$/, '').replace(/\[.+?\]/g, '[param]');
    const p = join(dir, e.name);
    if (e.isDirectory()) out.push(...normalize(p, prefix + '/' + seg));
    else out.push(prefix + '/' + seg);
  }
  return out;
}
const all = normalize('src/pages');
const dupes = all.filter((p, i) => all.indexOf(p) !== i);
if (dupes.length) throw new Error('semantically duplicate dynamic routes: ' + dupes.join(', '));

Prevention

When it happens

Trigger: Two non-prerendered routes whose patterns differ only in parameter names at the same positions — e.g. pages/blog/[slug].astro next to pages/blog/[id].astro, or [...slug] vs [...path] at the same spot — after the earlier static and prerender short-circuit checks did not return.

Common situations: Copy-pasting a dynamic page and renaming the parameter; merging route folders during a refactor; an injected admin route colliding semantically with a user route in output 'server'.

Related errors


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