withastro/astro · error · AstroError

InvalidI18nMiddlewareConfiguration

InvalidI18nMiddlewareConfiguration

Error message

The option `redirectToDefaultLocale` can be enabled only when `prefixDefaultLocale` is also set to `true`; otherwise, redirects might cause infinite loops. Enable the option `prefixDefaultLocale` to continue to use `redirectToDefaultLocale`, or ensure both are set to `false`.

What it means

Thrown (code `InvalidI18nMiddlewareConfiguration`) when the user-provided i18n middleware options set `prefixDefaultLocale: false` together with `redirectToDefaultLocale: true`. Without a prefix on the default locale, redirecting to it has no distinct URL to redirect to, producing infinite redirect loops. The check guards a combination the type system also forbids (`@ts-expect-error` marks the runtime check).

Source

Thrown at packages/astro/src/virtual-modules/i18n.ts:402

 * });
 *
 * export const onRequest = sequence(customLogic, middleware({
 * 	prefixDefaultLocale: true,
 * 	redirectToDefaultLocale: false
 * }))
 *
 * ```
 */
export let middleware: (customOptions: I18nMiddlewareOptions) => MiddlewareHandler;

if (i18n?.routing === 'manual') {
	middleware = (customOptions) => {
		if (
			customOptions.prefixDefaultLocale === false &&
			// @ts-expect-error types do not allow this but we also check at runtime
			customOptions.redirectToDefaultLocale === true
		) {
			throw new AstroError(InvalidI18nMiddlewareConfiguration);
		}
		strategy = toRoutingStrategy(customOptions, {});
		fallbackType = toFallbackType(customOptions);
		const manifest: SSRManifest['i18n'] = {
			...i18n,
			strategy,
			domainLookupTable: {},
			fallbackType,
			fallback: i18n.fallback,
			domains: i18n.domains,
		};
		return I18nInternals.createMiddleware(manifest, base, trailingSlash, format);
	};
} else {
	middleware = noop('middleware');
}

/**

View on GitHub (pinned to d081033d5f)

Solutions

  1. Set both options to false: `middleware({ prefixDefaultLocale: false, redirectToDefaultLocale: false })` — serve the default locale at root without redirect.
  2. Or set both to true: `middleware({ prefixDefaultLocale: true, redirectToDefaultLocale: true })` — prefix every locale including the default.
  3. Pick one consistent pair; never mix false/true for these two.

Example fix

// before
import { middleware } from 'astro:i18n';
export const onRequest = middleware({ prefixDefaultLocale: false, redirectToDefaultLocale: true });

// after
export const onRequest = middleware({ prefixDefaultLocale: false, redirectToDefaultLocale: false });
Defensive patterns

Strategy: validation

Validate before calling

function validateMiddlewareOptions(opts) {
  if (opts.prefixDefaultLocale === false && opts.redirectToDefaultLocale === true) {
    throw new Error('redirectToDefaultLocale requires prefixDefaultLocale: true');
  }
}

Prevention

When it happens

Trigger: Calling `middleware({ prefixDefaultLocale: false, redirectToDefaultLocale: true })` imported from `astro:i18n`. This branch only exists inside the `i18n.routing === 'manual'` middleware factory, so it requires manual routing to be enabled.

Common situations: Copy-pasting middleware options from a project that prefixed the default locale into one that does not. Disabling `prefixDefaultLocale` to serve the default locale at `/` but forgetting to also disable `redirectToDefaultLocale`. Misunderstanding that the two options are coupled.

Related errors


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