withastro/astro · error · AstroError

InvalidRedirectDestination

InvalidRedirectDestination

Error message

The redirect from "${from}" to "${to}" is invalid. The destination "${to}" does not match any existing route in your project.

What it means

When a `redirects` source contains dynamic params (e.g. `/old/[slug]`), the static build must substitute those params into the destination to emit one redirect file per value — which requires the destination to resolve to a real route. If the destination is not a URL, matches nothing in the route map, and the config prerenders by default, Astro throws `InvalidRedirectDestination` at manifest creation instead of failing later with a misleading error.

Solutions

  1. Fix the destination to an existing route (check `src/pages` for the exact pattern)
  2. Carry the dynamic segment over: `'/old/[slug]': '/posts/[slug]'`
  3. Use a full `https://…` URL for external targets (URLs skip route matching)
  4. Add a CI check that every non-URL redirect destination matches a page in `src/pages`

Example fix

// before
redirects: { '/old/[slug]': '/new-posts' }  // no /new-posts route exists

// after
redirects: { '/old/[slug]': '/posts/[slug]' }  // existing dynamic route
Defensive patterns

Strategy: validation

Validate before calling

// Pre-build check: every non-URL redirect destination must match a pages route
import { getRoutes } from './route-helpers.js'; // builds expected paths from src/pages
const known = await getRoutes(); // e.g. Set like '/posts/[slug]', '/about'
for (const [from, to] of Object.entries(redirects)) {
  const dest = typeof to === 'string' ? to : to.destination;
  if (URL.canParse(dest)) continue; // external, fine
  if (from.includes('[') && ![...known].some((r) => routeMatches(r, dest))) {
    throw new Error(`redirect ${from} → ${dest}: destination route not found`);
  }
}

Prevention

When it happens

Trigger: `redirects: { '/old/[slug]': '/new-posts' }` where no `/new-posts` route exists (typo or never created); renaming destination routes without updating the redirects map; destinations with leading-path typos like `/post/[slug]` vs `/posts/[slug]`.

Common situations: Maintaining a legacy redirect table as the site evolves; content migrations that move routes after redirects were written; large configs where a stale entry goes unnoticed until build.

Related errors


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

Appendix: source

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

		if (URL.canParse(destination) && !/^https?:\/\//.test(destination)) {
			throw new AstroError({
				...UnsupportedExternalRedirect,
				message: UnsupportedExternalRedirect.message(from, destination),
			});
		}

		const redirectRoute = routeMap.get(destination);

		// If the source has dynamic params and the redirect will be prerendered,
		// we need a valid redirectRoute to map them. Without it, the build will fail
		// later with a misleading error. Catch this early and provide a clear error message.
		if (
			params.length > 0 &&
			!redirectRoute &&
			!URL.canParse(destination) &&
			getPrerenderDefault(config)
		) {
			throw new AstroError({
				...InvalidRedirectDestination,
				message: InvalidRedirectDestination.message(from, destination),
			});
		}

		// If the source has dynamic params and the destination route exists but has
		// fewer params, the build will fail later with a misleading error (e.g.,
		// "getStaticPaths required" pointing at the wrong file). Catch this early.
		if (
			params.length > 0 &&
			redirectRoute &&
			!URL.canParse(destination) &&
			getPrerenderDefault(config)
		) {
			const destParams = redirectRoute.params ?? [];
			const missingParams = params.filter((p) => !destParams.includes(p));
			if (missingParams.length > 0) {
				throw new AstroError({

View on GitHub (pinned to e294953aa8)