{"record":{"id":"ec04fd443ad51f4d","repo":"mastra-ai/mastra","slug":"too-many-redirects-maximum-is-max-redirects","errorCode":null,"errorMessage":"Too many redirects. Maximum is ${MAX_REDIRECTS}.","messagePattern":"Too many redirects\\. Maximum is (.+?)\\.","errorType":"exception","errorClass":"WebFetchError","httpStatus":null,"severity":"error","filePath":"packages/core/src/tools/builtin/web-fetch.ts","lineNumber":218,"sourceCode":"    const request = requestModule.request(\n      url,\n      {\n        headers: {\n          'user-agent': 'Mastra Web Fetch Tool/1.0',\n          accept: 'text/html,text/plain,application/json,application/xml;q=0.9,*/*;q=0.8',\n        },\n        lookup: createLookup(),\n        timeout: TIMEOUT_MS,\n      },\n      response => {\n        void (async () => {\n          const location = response.headers.location;\n\n          if (location && response.statusCode && response.statusCode >= 300 && response.statusCode < 400) {\n            response.resume();\n\n            if (redirectsRemaining <= 0) {\n              throw new WebFetchError(`Too many redirects. Maximum is ${MAX_REDIRECTS}.`);\n            }\n\n            const nextUrl = parseHttpUrl(new URL(location, url).toString());\n            if (!nextUrl) {\n              throw new WebFetchError('Redirect target must use HTTP or HTTPS.');\n            }\n\n            resolve(await requestUrl(nextUrl, redirectsRemaining - 1));\n            return;\n          }\n\n          const { content, truncated } = await readBody(response);\n\n          resolve({\n            content,\n            truncated,\n            status: response.statusCode,\n            statusText: response.statusMessage,","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/tools/builtin/web-fetch.ts#L200-L236","documentation":"requestUrl follows HTTP 3xx redirects manually, up to MAX_REDIRECTS hops, decrementing redirectsRemaining each time. When a redirect response arrives and no redirect budget remains, the tool throws a WebFetchError because unbounded redirect following is both a hang/infinite-loop risk and an SSRF vector (long chains can be steered toward internal addresses).","triggerScenarios":"Fetching a URL that redirects more than MAX_REDIRECTS times: redirect chains, loops (A -> B -> A), cookie/auth flows redirecting repeatedly, short-link services stacked multiple deep, or misconfigured servers emitting endless 302s.","commonSituations":"Shortened URLs (bit.ly chains) embedded in agent prompts; expired sessions that keep redirecting to a login page that redirects back; misconfigured hosting where http->https->http loops.","solutions":["Resolve the final URL yourself and pass it directly (e.g. curl -ILs <url> to find the terminal URL, or use a URL-expansion service once).","Break redirect loops: fix the target server's redirect configuration (avoid http<->https or trailing-slash loops).","If the content genuinely needs many hops, fetch the intermediate hop(s) with separate webFetch calls.","For legitimate deep chains, raise MAX_REDIRECTS in a fork/patch of the tool — but confirm each hop stays on public hosts, since the limit is also a safety guard."],"exampleFix":"// before\nawait webFetchTool.execute({ context: { url: 'https://bit.ly/abc123' } }); // 6+ hop chain\n\n// after: resolve first, then fetch the terminal URL\nawait webFetchTool.execute({ context: { url: 'https://example.com/final/article' } });","handlingStrategy":"try-catch","validationCode":"// Resolve redirect chains ahead of time and fetch only the terminal URL\nasync function resolveFinalUrl(url: string, max = 5): Promise<string> {\n  let current = url;\n  for (let i = 0; i < max; i++) {\n    const res = await fetch(current, { redirect: 'manual', method: 'HEAD' });\n    if (res.status < 300 || res.status >= 400) return current;\n    const loc = res.headers.get('location');\n    if (!loc) return current;\n    current = new URL(loc, current).toString();\n  }\n  throw new Error('exceeds webFetch redirect limit');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await webFetchTool.execute({ context: { url } });\n} catch (err) {\n  if (err instanceof Error && /Too many redirects/.test(err.message)) {\n    // resolve the chain manually and retry once with the terminal URL\n    const finalUrl = await resolveFinalUrl(url);\n    return webFetchTool.execute({ context: { url: finalUrl } });\n  } else throw err;\n}","preventionTips":["Resolve short-link URLs before handing them to the tool.","Fix redirect loops on servers you control (check http->https and trailing-slash rules).","Watch for auth flows that bounce between login and target pages.","Track redirect depth in your own URL pipeline and cap it early."],"tags":["network","http","redirects","web-fetch"],"backgroundTag":"too-many-redirects","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}