{"record":{"id":"3a82dac06b88a823","repo":"hcengineering/platform","slug":"too-many-redirects","errorCode":null,"errorMessage":"Too many redirects","messagePattern":"Too many redirects","errorType":"exception","errorClass":"LinkPreviewError","httpStatus":null,"severity":"error","filePath":"pods/link-preview/src/parse.ts","lineNumber":268,"sourceCode":"        redirect: 'manual'\n      },\n      timeoutMs\n    )\n\n    if (!isRedirectStatus(response.status)) {\n      return { response, finalUrl: currentUrl }\n    }\n\n    const location = response.headers.get('location')\n    if (!isNonEmptyString(location)) {\n      return { response, finalUrl: currentUrl }\n    }\n\n    const nextUrl = new URL(location, currentUrl).href\n    currentUrl = nextUrl\n  }\n\n  throw new LinkPreviewError('Too many redirects')\n}\n\n// ============================================================================\n// Fetch Utilities\n// ============================================================================\n\nasync function fetchWithTimeout (url: string, options: RequestInit, timeoutMs: number): Promise<Response> {\n  const controller = new AbortController()\n  const timeoutId = setTimeout(() => {\n    controller.abort()\n  }, timeoutMs)\n\n  try {\n    const response = await fetch(url, {\n      ...options,\n      signal: controller.signal\n    })\n    return response","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/pods/link-preview/src/parse.ts#L250-L286","documentation":"LinkPreviewError thrown by fetchWithValidatedRedirects when a URL exceeds the allowed number of HTTP redirects. The library follows Location headers in a loop with a hard cap to prevent infinite redirect chains; exceeding the cap aborts with this error rather than looping forever.","triggerScenarios":"Calling the link preview fetch (via response) against a URL whose redirect chain is longer than the library's max, or URLs that redirect in a loop (A->B->A), or misconfigured servers emitting perpetual 301/302 responses.","commonSituations":"Sites with redirect loops caused by misconfigured reverse proxies (e.g. HTTPS redirect fighting with a load balancer), cookie-gated pages bouncing between endpoints, tracking URLs chained through many shorteners, or redirects pointing back to the same host with different casing/schemes that never resolve.","solutions":["Test the URL with curl -IL to inspect the redirect chain and confirm whether it loops or is merely long","Fix the server/proxy redirect loop (e.g. correct X-Forwarded-Proto handling) if you control the target","Use the final resolved URL (pre-resolve shorteners) so fewer hops remain","Increase the redirect limit if the library exposes configuration, or catch LinkPreviewError and skip the URL","Verify the URL scheme: http->https upgrade loops indicate mixed-content proxy misconfig"],"exampleFix":"// before\nawait fetchWithValidatedRedirects('http://example.com/start')\n// after\ntry {\n  const { response, finalUrl } = await fetchWithValidatedRedirects(inputUrl)\n} catch (e) {\n  if (e instanceof LinkPreviewError) {\n    // skip or pre-resolve finalUrl; mark URL as unreachable\n    return null\n  }\n  throw e\n}","handlingStrategy":"try-catch","validationCode":"function looksRedirectLoopSafe(url: string): boolean {\n  try { const u = new URL(url); return u.protocol === 'http:' || u.protocol === 'https:' } catch { return false }\n}","typeGuard":"function isLinkPreviewError(e: unknown): e is LinkPreviewError {\n  return e instanceof LinkPreviewError\n}","tryCatchPattern":"try {\n  const { response, finalUrl } = await fetchWithValidatedRedirects(url)\n} catch (e) {\n  if (e instanceof LinkPreviewError && e.message === 'Too many redirects') {\n    return null // skip unreachable URL\n  }\n  throw e\n}","preventionTips":["Pre-resolve shortener URLs before previewing","Check suspicious URLs with curl -IL for redirect loops","Skip URLs flagged with prior redirect failures","Keep a redirect-hop budget when crawling"],"tags":["network","http","redirects"],"backgroundTag":"too-many-redirects","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}