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}". A static route cannot be defined more than once.

What it means

Two routes in the manifest resolve to the exact same fully-static pattern (every segment static, e.g. about.astro and about/index.astro). Only one can ever match, so Astro warns (domain 'router') that the route is defined in both files and that a static route cannot be defined more than once.

Source

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

	a: RouteData,
	b: RouteData,
	_config: AstroConfig,
	logger: AstroLogger,
) {
	if (a.type === 'fallback' || b.type === 'fallback') {
		// If either route is a fallback route, they don't collide.
		// Fallbacks are always added below other routes exactly to avoid collisions.
		return;
	}

	if (
		a.route === b.route &&
		a.segments.every(isStaticSegment) &&
		b.segments.every(isStaticSegment)
	) {
		// If both routes are the same and completely static they are guaranteed to collide
		// such that one of them will never be matched.
		logger.warn(
			'router',
			`The route "${a.route}" is defined in both "${a.component}" and "${b.component}". A static route cannot be defined more than once.`,
		);
		logger.warn(
			'router',
			'A collision will result in a hard error in following versions of Astro.',
		);
		return;
	}

	if (a.prerender || b.prerender) {
		// If either route is prerendered, it is impossible to know if they collide
		// at this stage because it depends on the parameters returned by `getStaticPaths`.
		return;
	}

	if (a.segments.length !== b.segments.length) {
		// If the routes have different number of segments, they cannot perfectly overlap

View on GitHub (pinned to e294953aa8)

Solutions

  1. Delete or rename one of the two colliding files/routes
  2. Render markdown twins through a single dynamic route ([...slug].astro with content collections) instead of a duplicate static page
  3. Audit integration injectRoute patterns against your filesystem routes

Example fix

# before — both normalize to /about
src/pages/about.astro
src/pages/about/index.astro

# after
src/pages/about/index.astro  # keep exactly one
Defensive patterns

Strategy: validation

Validate before calling

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

Prevention

When it happens

Trigger: Files under pages/ that normalize to the same URL: foo.astro plus foo/index.astro, index.astro plus index.md in one directory, or two injected static routes with the same pattern — and neither route being a fallback route.

Common situations: Adding index.md docs beside an existing .astro page; refactoring nested folder structures without deleting old files; copy-pasting a page under a new name; injected routes from integrations duplicating filesystem routes.

Related errors


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