mastra-ai/mastra · error · WebFetchError
Redirect target must use HTTP or HTTPS.
Error message
Redirect target must use HTTP or HTTPS.
What it means
When a 3xx response carries a Location header, requestUrl builds the absolute redirect target with new URL(location, url) and re-validates it through parseHttpUrl, which only accepts http: and https: schemes. If the redirect target is not HTTP(S) — e.g. a Location pointing to ftp:, file:, data:, or a malformed relative reference that yields a non-HTTP URL — the tool throws a WebFetchError instead of following it, blocking protocol-redirection attacks.
Source
Thrown at packages/core/src/tools/builtin/web-fetch.ts:223
accept: 'text/html,text/plain,application/json,application/xml;q=0.9,*/*;q=0.8',
},
lookup: createLookup(),
timeout: TIMEOUT_MS,
},
response => {
void (async () => {
const location = response.headers.location;
if (location && response.statusCode && response.statusCode >= 300 && response.statusCode < 400) {
response.resume();
if (redirectsRemaining <= 0) {
throw new WebFetchError(`Too many redirects. Maximum is ${MAX_REDIRECTS}.`);
}
const nextUrl = parseHttpUrl(new URL(location, url).toString());
if (!nextUrl) {
throw new WebFetchError('Redirect target must use HTTP or HTTPS.');
}
resolve(await requestUrl(nextUrl, redirectsRemaining - 1));
return;
}
const { content, truncated } = await readBody(response);
resolve({
content,
truncated,
status: response.statusCode,
statusText: response.statusMessage,
contentType: Array.isArray(response.headers['content-type'])
? response.headers['content-type'][0]
: (response.headers['content-type'] ?? null),
url: url.toString(),
ok: response.statusCode ? response.statusCode >= 200 && response.statusCode < 300 : false,View on GitHub (pinned to 75dd419e61)
Solutions
- Inspect the response chain (curl -IL <url>) to see the non-HTTP Location value; if it's your server, fix the redirect to use https://.
- Fetch the intended HTTP(S) target directly instead of following the redirecting entry URL.
- Treat this on attacker-influenced URLs as malicious input: reject or sanitize the URL before passing it to webFetch.
- If a legacy ftp mirror is the real target, retrieve it outside the web-fetch tool (the tool is HTTP-only by design).
Example fix
// before
await webFetchTool.execute({ context: { url: 'https://legacy.example.com/downloads' } }); // Location: ftp://mirror...
// after: fetch an HTTP(S) endpoint directly
await webFetchTool.execute({ context: { url: 'https://mirror.example.com/downloads' } }); Defensive patterns
Strategy: try-catch
Validate before calling
// Only allow http(s) URLs, and check redirect targets the server might return
function isHttpUrl(raw: string): boolean {
try {
const u = new URL(raw);
return u.protocol === 'http:' || u.protocol === 'https:';
} catch { return false; }
} Type guard
function isHttpScheme(u: URL): boolean {
return u.protocol === 'http:' || u.protocol === 'https:';
} Try / catch
try {
await webFetchTool.execute({ context: { url } });
} catch (err) {
if (err instanceof Error && err.message.includes('Redirect target must use HTTP or HTTPS')) {
console.warn(`redirect to non-HTTP scheme from ${url}; refusing`);
} else throw err;
} Prevention
- Pre-screen URLs from untrusted/agent-generated input for scheme safety.
- Treat non-HTTP redirect targets as a sign of a malicious or broken endpoint.
- Fix servers that redirect to ftp:/file: mirrors; expose an https endpoint instead.
- Log the offending URL so users can see which host emitted the bad Location header.
When it happens
Trigger: A server responding 3xx with a Location header whose scheme is not http/https (ftp:, file:, data:, javascript:), or a Location that cannot be parsed into an http(s) URL (e.g. an invalid relative reference or a URL with a disallowed scheme after base resolution).
Common situations: Malicious or misconfigured endpoints redirecting to non-HTTP schemes; legacy servers redirecting to ftp:// mirrors; attacker-controlled URLs in agent input attempting scheme-redirection SSRF or local-file reads.
Related errors
- Too many redirects. Maximum is ${MAX_REDIRECTS}.
- Slack OAuth HTTP error: ${tokenResponse.status} ${tokenRespo
- Failed to stream background tasks: ${response.statusText}
- Failed to stream agent builder action: ${response.statusText
- Failed to observe agent builder action stream: ${response.st
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/d8a3d581d0bd8e06.
Report an issue: GitHub.