{"record":{"id":"8d61fd3d0e4355dc","repo":"heygen-com/hyperframes","slug":"failed-to-download-url-empty-response-body","errorCode":null,"errorMessage":"Failed to download ${url}: empty response body","messagePattern":"Failed to download (.+?): empty response body","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/cloud/download.ts","lineNumber":50,"sourceCode":"/**\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\n          ? options.signal.reason\n          : new Error(\"Download aborted\");\n      }","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/cloud/download.ts#L32-L68","documentation":"Thrown by downloadToFile when the response is res.ok (2xx) but res.body is null/undefined. This is an unusual server behavior — a success status with no body — that the client refuses to silently turn into a zero-byte file. Like the HTTP-error branch it fires before destPath is created, so no partial artifact appears.","triggerScenarios":"A server returns 200 with Content-Length: 0 and no body, or a fetch implementation/runtime where the stream is absent (some non-browser runtimes, mocked fetches in tests, or a HEAD-shaped response to a GET).","commonSituations":"A misconfigured object store or CDN returning an empty 200 for a zero-size object; a test double that returns { ok: true } without a body; a streaming transport that closed the connection before emitting any chunk.","solutions":["Verify the URL actually points at a non-empty object (curl -i and check Content-Length).","If the object genuinely is empty, handle the empty case explicitly upstream rather than relying on downloadToFile.","For test doubles, ensure the mocked Response includes a non-empty ReadableStream body.","Re-reserve the asset URL if the store returned a degenerate response for a known-nonempty object."],"exampleFix":"// before: test mock returns ok but no body\nfetchMock.mockResponse('', { status: 200 });\n\n// after: include a body stream\nfetchMock.mockResponse(Buffer.from(bytes), { status: 200 });","handlingStrategy":"validation","validationCode":"async function assertNonEmptyBody(url: string): Promise<void> {\n  const res = await fetch(url);\n  if (!res.ok) throw new Error(`HTTP ${res.status}`);\n  if (!res.body) throw new Error('server returned 2xx with no body — empty or misconfigured object');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await downloadToFile(url, dest);\n} catch (err) {\n  if (/empty response body/.test(String(err?.message))) {\n    // treat as a degenerate object; re-reserve or skip\n  } else throw err;\n}","preventionTips":["For test mocks, always include a ReadableStream body on the Response.","Verify the object is non-empty before issuing a presigned download.","Treat a 2xx-with-no-body as a server/store defect worth reporting."],"tags":["network","download","cloud","edge-case"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}