JuliusBrussee/caveman · error · TypeError
fetch failed: unexpected redirect
Error message
fetch failed: unexpected redirect
What it means
When the caller passes redirect: "error", the proxy-aware fetch treats any redirectable 3xx response as a hard failure instead of following it, throwing this TypeError. It mirrors the WHATWG fetch spec's 'error' redirect mode.
Source
Thrown at packages/cli/src/proxy-fetch.ts:353
body: buffered,
signal: request.signal ?? null,
};
let hop = resolveProxyUrl(current.url, env);
for (let redirects = 0; ; redirects += 1) {
const response = hop ? await sendThroughProxy(current, hop) : await baseFetch(current.url, {
method: current.method,
headers: current.headers as HeadersInit,
body: asBodyInit(current.body),
signal: current.signal,
redirect: "manual",
});
const location = response.headers.get("location");
const redirectable = [301, 302, 303, 307, 308].includes(response.status);
if (!redirectable || !location || request.redirect === "manual") return response;
if (redirects >= MAX_REDIRECTS) throw new TypeError("fetch failed: too many redirects");
if (request.redirect === "error") throw new TypeError("fetch failed: unexpected redirect");
const next = new URL(location, current.url);
const method = nextMethod(response.status, current.method);
const bodyDropped = method !== current.method;
void response.body?.cancel();
current = {
method,
url: next,
headers: redirectedHeaders(current.headers, current.url, next, bodyDropped),
body: bodyDropped ? null : current.body,
signal: current.signal,
};
hop = resolveProxyUrl(next, env);
}
} as typeof fetch;
}
/**View on GitHub (pinned to 5184b3d11a)
Solutions
- Update the request URL to the current (non-redirecting) endpoint.
- Remove redirect: "error" (default is "follow") if automatic redirect handling is acceptable.
- Follow the redirect manually with redirect: "manual" and inspect response.headers.get("location").
- Ask the server owner to stop redirecting that route if it should be stable.
Example fix
// before
await fetch("http://api.example.com/v1/things", { redirect: "error" }); // 301 to https
// after
await fetch("https://api.example.com/v1/things", { redirect: "error" }); // use the final URL Defensive patterns
Strategy: try-catch
Type guard
function isUnexpectedRedirect(e: unknown): boolean { return e instanceof TypeError && e.message.includes("unexpected redirect"); } Try / catch
try {
const res = await fetch(url, { redirect: "error" });
} catch (e) {
if (e instanceof TypeError && e.message.includes("unexpected redirect")) {
console.error(`${url} redirected; update to the final URL or allow redirects`);
} else throw e;
} Prevention
- Use redirect: "error" only on URLs you control and have pinned to final destinations.
- Periodically curl endpoints to detect new redirects after server changes.
- Default to redirect: "follow" unless strict no-redirect semantics are required.
- Update hardcoded URLs after http->https migrations.
When it happens
Trigger: Calling fetch (installed by installProxyAwareFetch) with redirect: "error" while the server responds 301/302/303/307/308 with a Location header.
Common situations: Hardening-sensitive code that opts into redirect:"error" then hits an endpoint that has moved (http->https upgrade, trailing-slash redirect, renamed API route); stale bookmarks or config pointing at old URLs.
Related errors
- fetch failed: too many redirects
- tool search failed with HTTP ${response.status}
- device authorization failed: HTTP ${codeResponse.status}
- AbortError
- cave_sandbox_network_egress_unbounded
AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-31).
Data as JSON: /api/errors/43c322784b3775f2.
Report an issue: GitHub.