withastro/astro · error · AstroError
IncorrectStrategyForI18n
IncorrectStrategyForI18n
Error message
The function `${functionName}` can only be used when the `i18n.routing.strategy` is set to `"manual"`. What it means
Several exports of the astro:i18n virtual module — middleware, notFound, requestHasLocale, redirectToDefaultLocale, and redirectToFallback — exist only when you take full control of routing by setting i18n.routing to "manual" in astro.config. With any other routing configuration these functions are replaced with stubs that throw IncorrectStrategyForI18n, naming the function you called. This is intentional: with automatic routing Astro handles these concerns, so the manual APIs do not apply.
Solutions
- Set i18n: { routing: 'manual' } in astro.config.mjs if you truly want to drive routing yourself
- Otherwise remove the manual-only calls and let automatic routing plus fallback configuration handle it
- If you keep automatic routing but need custom logic, write a normal src/middleware.ts without the astro:i18n helpers
Example fix
// before — astro.config.mjs
export default defineConfig({
i18n: { defaultLocale: 'en', locales: ['en', 'es'], routing: { prefixDefaultLocale: true } },
});
// after
export default defineConfig({
i18n: { defaultLocale: 'en', locales: ['en', 'es'], routing: 'manual' },
}); Defensive patterns
Strategy: validation
Validate before calling
// astro.config.mjs — fail at config load instead of at request time
const config = defineConfig({
i18n: { defaultLocale: 'en', locales: ['en', 'es'], routing: 'manual' },
});
// middleware(), notFound(), requestHasLocale(), redirectToDefaultLocale(),
// redirectToFallback() from 'astro:i18n' all require routing: 'manual'
if (config.i18n?.routing !== 'manual' && process.env.ASTRO_I18N_MANUAL_HELPERS) {
throw new Error('astro:i18n manual helpers require i18n.routing: "manual"');
} Type guard
const isManualI18nRouting = (routing: unknown): routing is 'manual' => routing === 'manual';
Prevention
- Set i18n.routing: 'manual' in astro.config before importing the manual helpers from astro:i18n
- With automatic routing, do not call middleware/notFound/requestHasLocale/redirectToDefaultLocale/redirectToFallback at all
- Add a comment in src/middleware.ts noting which routing mode it assumes
When it happens
Trigger: Calling middleware(...) from astro:i18n in src/middleware.ts while i18n.routing is unset or set to an options object; calling redirectToDefaultLocale(context), notFound(context), requestHasLocale(context), or redirectToFallback(...) under non-manual routing.
Common situations: Copy-pasting the manual i18n middleware example from the docs without switching routing to 'manual'; migrating configs across Astro majors where routing options moved between string and object forms.
Related errors
- InvalidI18nMiddlewareConfiguration
- MissingMiddlewareForInternationalization
- MissingIndexForInternationalizationError
- MissingLocaleError
- `Astro.session` was accessed but no session storage is…
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/3e81f5514f7cdb8c.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/virtual-modules/i18n.ts:35
import type { MiddlewareHandler } from '../types/public/common.js';
import type { AstroConfig, ValidRedirectStatus } from '../types/public/config.js';
import type { APIContext } from '../types/public/context.js';
import type { ClientDeserializedManifest } from '../types/public/index.js';
const { trailingSlash, site, i18n, build } = config as ClientDeserializedManifest;
const { format } = build;
const isBuild = import.meta.env.PROD;
const { defaultLocale, locales, domains, fallback, routing } = i18n!;
const base = import.meta.env.BASE_URL;
let strategy = toRoutingStrategy(routing, domains);
let fallbackType = toFallbackType(routing);
export type GetLocaleOptions = I18nInternals.GetLocaleOptions;
const noop = (method: string) =>
function () {
throw new AstroError({
...IncorrectStrategyForI18n,
message: IncorrectStrategyForI18n.message(method),
});
};
/**
* @param locale A locale
* @param path An optional path to add after the `locale`.
* @param options Customise the generated path
*
* Returns a _relative_ path with passed locale.
*
* ## Errors
*
* Throws an error if the locale doesn't exist in the list of locales defined in the configuration.
*
* ## Examples
*View on GitHub (pinned to 52e6c34790)