denoland/deno · error · RangeError
Invalid redirect status code: ${status}
Error message
Invalid redirect status code: ${status} What it means
Response.redirect(url, status = 302) parses the URL, then requires redirectStatus(status) — true only for 301, 302, 303, 307, and 308. Any other value (including other 3xx codes like 300, 304, 306, or non-3xx values) throws RangeError 'Invalid redirect status code: <status>'.
Source
Thrown at ext/fetch/23_response.js:680
initializeResponseBase(response, inner, "immutable");
maybeSetServeNativeFromInner(response);
return response;
}
/**
* @param {string} url
* @param {number} status
* @returns {Response}
*/
static redirect(url, status = 302) {
const prefix = "Failed to execute 'Response.redirect'";
url = webidlConvertersUSVString(url, prefix, "Argument 1");
status = webidlConvertersUnsignedShort(status, prefix, "Argument 2");
const baseURL = getLocationHref();
const parsedURL = new URL(url, baseURL);
if (!redirectStatus(status)) {
throw new RangeError(`Invalid redirect status code: ${status}`);
}
const inner = newInnerResponse(status);
inner.type = "default";
ArrayPrototypePush(inner.headerList, ["Location", parsedURL.href]);
const response = webidl.createBranded(Response);
initializeResponseBase(response, inner, "immutable");
maybeSetServeNativeFromInner(response);
return response;
}
/**
* @param {any} data
* @param {ResponseInit} init
* @returns {Response}
*/
static json(data = undefined, init = undefined) {
const prefix = "Failed to execute 'Response.json'";
data = webidlConvertersAny(data);View on GitHub (pinned to 89f33cbef2)
Solutions
- Use one of 301, 302, 303, 307, 308 — prefer 302/308 for temporary/permanent generic redirects
- Validate status against a Set([301,302,303,307,308]) before calling Response.redirect, defaulting to 302
- When forwarding upstream responses, use `new Response(upstream.body, upstream)` instead of Response.redirect if the status may be non-redirect
Example fix
// before return Response.redirect(nextUrl, upstreamStatus); // 300/304 throw // after const REDIRECTS = new Set([301, 302, 303, 307, 308]); const status = REDIRECTS.has(upstreamStatus) ? upstreamStatus : 302; return Response.redirect(nextUrl, status);
Defensive patterns
Strategy: type-guard
Validate before calling
const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
function redirect(url, status) {
return Response.redirect(url, REDIRECT_STATUSES.has(status) ? status : 302);
} Type guard
/** @param {unknown} s */
function isValidRedirectStatus(s) {
return [301, 302, 303, 307, 308].includes(Number(s));
} Prevention
- Restrict redirect statuses to 301/302/303/307/308 via a Set lookup with a 302 default
- Never forward upstream statuses blindly into Response.redirect
- For non-redirect upstream statuses, pass the Response through instead of rebuilding it
When it happens
Trigger: Response.redirect('/login', 3021), Response.redirect(url, 304), or a status read from a config map that used a redirect-status placeholder like 0/undefined after failed lookup.
Common situations: Config-driven redirects where the code came from user input or a CMS; forwarding an upstream status verbatim into Response.redirect (upstream 304/300 then fails); typos like 3088.
Related errors
- The status provided (${init.status}) is not equal to 101 and
- Response with null body status cannot have body
- Cannot change header: headers are immutable
- Cannot read url: request closed
- Invalid status text: "${init.statusText}"
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/0f003aa98ba5977c.
Report an issue: GitHub.