angular/angular-cli · error · Error
Invalid redirect status code: ${status}. Please use one of t
Error message
Invalid redirect status code: ${status}. Please use one of the following redirect response codes: ${[...VALID_REDIRECT_RESPONSE_CODES.values()].join(', ')}. What it means
`createRedirectResponse` builds an HTTP redirect `Response` for SSR, and only a fixed set of redirect status codes (301, 302, 303, 307, 308 — `VALID_REDIRECT_RESPONSE_CODES`) is allowed. In dev mode (`ngDevMode`) it throws this error if the provided `status` is outside that set, catching invalid redirect codes (e.g. 200 or 404) early.
Source
Thrown at packages/angular/ssr/src/utils/redirect.ts:41
return VALID_REDIRECT_RESPONSE_CODES.has(code);
}
/**
* Creates an HTTP redirect response with a specified location and status code.
*
* @param location - The URL to which the response should redirect.
* @param status - The HTTP status code for the redirection. Defaults to 302 (Found).
* See: https://developer.mozilla.org/en-US/docs/Web/API/Response/redirect_static#status
* @param headers - Additional headers to include in the response.
* @returns A `Response` object representing the HTTP redirect.
*/
export function createRedirectResponse(
location: string,
status = 302,
headers?: Record<string, string> | Headers,
): Response {
if (ngDevMode && !isValidRedirectResponseCode(status)) {
throw new Error(
`Invalid redirect status code: ${status}. ` +
`Please use one of the following redirect response codes: ${[...VALID_REDIRECT_RESPONSE_CODES.values()].join(', ')}.`,
);
}
const resHeaders = headers instanceof Headers ? headers : new Headers(headers);
if (ngDevMode && resHeaders.has('location')) {
// eslint-disable-next-line no-console
console.warn(
`Location header "${resHeaders.get('location')}" will be ignored and set to "${location}".`,
);
}
// Ensure unique values for Vary header
const varyArray = resHeaders.get('Vary')?.split(',') ?? [];
const varySet = new Set(['X-Forwarded-Prefix']);
for (const vary of varyArray) {
const value = vary.trim();View on GitHub (pinned to bb72145f9a)
Solutions
- Use one of the allowed codes: 301 (permanent), 302 (default temporary), 303, 307, or 308.
- Replace invalid codes: use 302/307 for temporary redirects and 301/308 for permanent ones (e.g. change 304 or 200 to 302).
- If the status comes from configuration/env, validate/clamp it against `isValidRedirectResponseCode()` before calling.
- Note the check only runs when `ngDevMode` is truthy — production builds may silently produce a broken response, so fix at the source, not just in dev.
Example fix
// before const res = createRedirectResponse(targetUrl, 304); // after const res = createRedirectResponse(targetUrl, 302); // or 301/303/307/308
Defensive patterns
Strategy: validation
Validate before calling
const VALID = new Set([301, 302, 303, 307, 308]);
if (!VALID.has(status)) {
throw new Error(`status must be one of ${[...VALID].join(', ')}, got ${status}`);
}
const res = createRedirectResponse(location, status, headers); Type guard
function isRedirectStatus(s: number): s is 301 | 302 | 303 | 307 | 308 {
return [301, 302, 303, 307, 308].includes(s);
} Try / catch
try {
return createRedirectResponse(location, status, headers);
} catch (e) {
if (String(e?.message).startsWith('Invalid redirect status code')) {
return createRedirectResponse(location, 302, headers); // safe default
}
throw e;
} Prevention
- Only use 301, 302, 303, 307, 308 for redirects; never pass arbitrary 3xx/2xx codes.
- Type redirect statuses as `301|302|303|307|308` union instead of `number`.
- If status comes from config, validate it with `isValidRedirectResponseCode()` first.
- Remember the throw only happens in ngDevMode — test redirects in a dev build, don't rely on production to catch it.
When it happens
Trigger: Passing a custom `status` to `createRedirectResponse(location, status)` (or the underlying redirect helper) that is not one of the valid redirect codes, e.g. `createRedirectResponse('/login', 200)` or a status read from config as an arbitrary number.
Common situations: Language-based redirects (`redirectBasedOnAcceptLanguage`) configured with a wrong default; mapping backend/legacy status codes directly to SSR redirects; typos like 3011 or 304; using 303/308 incorrectly assuming all 3xx are allowed.
Related errors
- Header "${headerName}" with value "${headerValue}" is not al
- Header "x-forwarded-port" must be a numeric value.
- Header "x-forwarded-proto" must be either "http" or "https".
- Header "x-forwarded-prefix" is invalid. It must start with a
- "${TRUST_ALL_PROXY_HEADERS}" is not allowed as a value for t
AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30).
Data as JSON: /api/errors/e0099a55866b31b3.
Report an issue: GitHub.