{"record":{"id":"a51e71ba70717719","repo":"heygen-com/hyperframes","slug":"failed-to-download-url-http-res-status-re","errorCode":null,"errorMessage":"Failed to download ${url}: HTTP ${res.status} ${res.statusText}","messagePattern":"Failed to download (.+?): HTTP (.+?) (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/cloud/download.ts","lineNumber":47,"sourceCode":"  bytes: number;\n}\n\n/**\n * Stream `url` into `destPath`. Creates the parent directory if needed,\n * truncates any existing file at the destination, and deletes the\n * partial output on any error so the caller never observes a corrupt\n * file at the returned path.\n */\n// fallow-ignore-next-line complexity\nexport async function downloadToFile(\n  url: string,\n  destPath: string,\n  options: DownloadOptions = {},\n): Promise<DownloadResult> {\n  const fetchImpl = options.fetchImpl ?? fetch;\n  const res = await fetchImpl(url, { signal: options.signal });\n  if (!res.ok) {\n    throw new Error(`Failed to download ${url}: HTTP ${res.status} ${res.statusText}`);\n  }\n  if (!res.body) {\n    throw new Error(`Failed to download ${url}: empty response body`);\n  }\n\n  mkdirSync(dirname(destPath), { recursive: true });\n\n  const totalHeader = res.headers.get(\"content-length\");\n  const total = totalHeader ? Number.parseInt(totalHeader, 10) : undefined;\n  const totalOpt = total !== undefined && Number.isFinite(total) ? total : undefined;\n\n  const file = createWriteStream(destPath);\n  let bytes = 0;\n  let errored = false;\n  try {\n    for await (const chunk of res.body as unknown as AsyncIterable<Uint8Array>) {\n      if (options.signal?.aborted) {\n        throw options.signal.reason instanceof Error","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/cloud/download.ts#L29-L65","documentation":"Thrown by downloadToFile when the fetch resolves with res.ok === false. The message surfaces the URL, HTTP status, and statusText verbatim so the caller can tell an expired presigned URL (403), a missing object (404), or a server-side failure (5xx) apart. It fires before any file is opened, so no partial artifact is created at destPath.","triggerScenarios":"Downloading a presigned S3/GCS URL after its expiry window (403 Forbidden), fetching an asset whose object was deleted between manifest generation and download (404), a 5xx from the object store, or any redirect/auth failure that returns non-2xx.","commonSituations":"A presigned URL sits idle past its TTL (common when the asset was reserved, then the download was queued behind other work); the cloud asset was garbage-collected; a network appliance rewrites the response to a 401 captive-portal page.","solutions":["Refetch a fresh asset/URL via `hyperframes cloud get` (or the equivalent reserve call) and retry — most cases are expired presigned URLs.","Confirm the object still exists in the cloud (status 404 means it is gone; you must re-create or re-upload it).","For 5xx, retry with backoff a couple of times before treating as a hard failure.","If 401/403 persists on a freshly issued URL, check credentials and clock skew on the signing host."],"exampleFix":"// before: a possibly-stale URL reused across retries\nawait downloadToFile(staleUrl, dest);\n\n// after: refresh the presigned URL when the download 403s\ntry {\n  await downloadToFile(url, dest);\n} catch (err) {\n  if (/HTTP 403/.test(String(err?.message))) {\n    const fresh = await client.getAssetUrl(id);\n    await downloadToFile(fresh, dest);\n  } else throw err;\n}","handlingStrategy":"retry","validationCode":"async function assertDownloadable(url: string): Promise<void> {\n  const res = await fetch(url, { method: 'GET' });\n  if (!res.ok) throw new Error(`preflight: HTTP ${res.status}`);\n  res.body?.cancel();\n}","typeGuard":null,"tryCatchPattern":"async function downloadWithRefresh(getUrl: () => Promise<string>, dest: string) {\n  for (let attempt = 0; attempt < 3; attempt++) {\n    try {\n      await downloadToFile(await getUrl(), dest);\n      return;\n    } catch (err) {\n      if (/HTTP 40[03]/.test(String(err?.message)) && attempt < 2) continue;\n      throw err;\n    }\n  }\n}","preventionTips":["Start the download immediately after reserving a presigned URL to stay inside its TTL.","Re-reserve the URL on 403/404 instead of retrying the stale one.","For 5xx, retry with backoff a small, bounded number of times."],"tags":["network","download","cloud","presigned-url"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}