{"record":{"id":"b0f47287913eb30a","repo":"vercel-labs/skills","slug":"download-response-has-no-body","errorCode":null,"errorMessage":"Download response has no body","messagePattern":"Download response has no body","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/download-source.ts","lineNumber":105,"sourceCode":"    redirect: 'follow',\n  });\n\n  if (!response.ok) {\n    throw new Error(`Download failed with HTTP ${response.status}`);\n  }\n\n  const contentLength = response.headers.get('content-length');\n  if (contentLength) {\n    const parsed = Number.parseInt(contentLength, 10);\n    if (Number.isFinite(parsed) && parsed > limits.downloadMaxBytes) {\n      throw new Error(\n        `Download is larger than ${limits.downloadMaxBytes} bytes. Set SKILLS_DOWNLOAD_MAX_BYTES to override.`\n      );\n    }\n  }\n\n  if (!response.body) {\n    throw new Error('Download response has no body');\n  }\n\n  let downloaded = 0;\n  const limitStream = new TransformStream<Uint8Array, Uint8Array>({\n    transform(chunk, controller) {\n      downloaded += chunk.byteLength;\n      if (downloaded > limits.downloadMaxBytes) {\n        throw new Error(\n          `Download is larger than ${limits.downloadMaxBytes} bytes. Set SKILLS_DOWNLOAD_MAX_BYTES to override.`\n        );\n      }\n      controller.enqueue(chunk);\n    },\n  });\n\n  await pipeline(response.body.pipeThrough(limitStream), createWriteStream(targetFile));\n}\n","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/vercel-labs/skills/blob/435076e78988e1e6ec40d00b0b1d76bdbbc5419a/src/download-source.ts#L87-L123","documentation":"After a successful (2xx) response with an acceptable Content-Length, downloadToFile requires response.body to be present; a null body means the server declared success but provided no stream to read. In practice this happens with unusual server responses, broken proxies, or runtimes whose fetch implementation returns empty bodies for certain status/header combinations.","triggerScenarios":"A 2xx response with a null/absent body — misbehaving CDNs, HEAD-like responses to GET, HTTP 204/304-style replies from redirects, or a fetch polyfill that doesn't populate body.","commonSituations":"Corporate proxies stripping bodies, server misconfiguration returning empty 200s, or unit tests with mocked fetch responses that omit the body field.","solutions":["curl -v the URL and inspect whether a body is actually returned","Retry — some proxies/CDNs intermittently return empty bodies","If a custom fetch/mocked fetch is in play (tests), ensure the mock sets body","Report to the server owner if the origin genuinely returns empty 200s"],"exampleFix":"# before\nskills add https://example.com/skills.zip  # Download response has no body\n\n# after\ncurl -v https://example.com/skills.zip -o skills.zip  # inspect\nskills add ./skills.zip","handlingStrategy":"retry","validationCode":"// Verify the endpoint returns a body before delegating\nasync function hasBody(url: string): Promise<boolean> {\n  const res = await fetch(url, { method: 'GET', headers: { Range: 'bytes=0-0' } });\n  return res.body != null;\n}","typeGuard":"null","tryCatchPattern":"try {\n  await downloadSource(url);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Download response has no body') {\n    await sleep(1000); // transient proxy behavior — retry once\n    return downloadSource(url);\n  }\n  throw err;\n}","preventionTips":["Prefer static asset URLs over dynamic endpoints","In tests, always set body on mocked fetch Responses"],"tags":["network","download","http","empty-response"],"backgroundTag":"empty-response-body","analyzedSha":"435076e78988e1e6ec40d00b0b1d76bdbbc5419a","analyzedAt":"2026-08-28T17:47:53.369Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}