{"record":{"id":"e2dfcb98ece5d677","repo":"Hmbown/CodeWhale","slug":"too-many-redirects-starting-at-url","errorCode":null,"errorMessage":"too many redirects starting at ${url}","messagePattern":"too many redirects starting at (.+?)","errorType":"exception","errorClass":"NonRetryableError","httpStatus":null,"severity":"error","filePath":"npm/codewhale/scripts/install.js","lineNumber":985,"sourceCode":"// Public download primitives (now retry + progress aware)\n// ────────────────────────────────────────────────────────────────────────────\n\nasync function followRedirects(url, opts = {}) {\n  const maxRedirects = 10;\n  let current = url;\n  for (let hop = 0; hop < maxRedirects; hop++) {\n    const result = await httpRequest(current, opts);\n    if (result.redirect) {\n      try {\n        current = new URL(result.redirect, current).toString();\n      } catch {\n        current = result.redirect;\n      }\n      continue;\n    }\n    return result;\n  }\n  throw new NonRetryableError(`too many redirects starting at ${url}`);\n}\n\nfunction streamToFile(response, destination, progress, signal) {\n  return new Promise((resolve, reject) => {\n    const sink = createWriteStream(destination);\n    let done = false;\n    const onAbort = () => {\n      try {\n        response.destroy();\n      } catch {\n        // ignore\n      }\n      finish(abortError());\n    };\n    const finish = (err) => {\n      if (done) return;\n      done = true;\n      if (signal) {","sourceCodeStart":967,"sourceCodeEnd":1003,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/npm/codewhale/scripts/install.js#L967-L1003","documentation":"The installer's download loop follows redirect responses with a bounded iteration count; `resolveDownload` throws this NonRetryableError when the starting URL still has not yielded a final (non-redirect) response after the limit. It protects against redirect loops or endless redirect chains on a misconfigured release mirror.","triggerScenarios":"Fetching an asset/manifest URL whose server responds with a redirect chain longer than the allowed maximum, or two URLs redirecting to each other in a loop.","commonSituations":"CODEWHALE_RELEASE_BASE_URL pointed at a mirror that bounces between http/https or adds trailing-slash redirects forever; a corporate proxy rewriting redirects; a mirror serving 302s to a login page in a loop.","solutions":["Open the failing URL in a browser/curl and inspect where the redirect chain leads; fix CODEWHALE_RELEASE_BASE_URL to point directly at the artifact host.","Retry the install later if the mirror is misbehaving or mid-migration.","Switch to the default release source by unsetting CODEWHALE_RELEASE_BASE_URL / CODEWHALE_USE_CNB_MIRROR.","Clear an http->https loop by using the final https URL explicitly."],"exampleFix":"// before\nCODEWHALE_RELEASE_BASE_URL=http://old-mirror.example.com/codewhale\n// after (point directly at final host)\nCODEWHALE_RELEASE_BASE_URL=https://releases.example.com/codewhale","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await download(url);\n} catch (err) {\n  if (err.message.startsWith('too many redirects')) {\n    // fix mirror URL or fall back to the default release source\n    process.env.CODEWHALE_RELEASE_BASE_URL = DEFAULT_BASE_URL;\n  } else throw err;\n}","preventionTips":["Point CODEWHALE_RELEASE_BASE_URL at a host that serves artifacts directly (no redirect chains).","curl -IL the mirror URL once to verify it terminates in a 200.","Avoid http->https bouncing mirrors."],"tags":["network","http","redirect","installer","download"],"backgroundTag":"http-error-response","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}