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

  1. Use one of 301, 302, 303, 307, 308 — prefer 302/308 for temporary/permanent generic redirects
  2. Validate status against a Set([301,302,303,307,308]) before calling Response.redirect, defaulting to 302
  3. 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

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


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/0f003aa98ba5977c. Report an issue: GitHub.