{"record":{"id":"3a9074c9dbea0301","repo":"santifer/career-ops","slug":"the-index-response-carried-no-readable-body-url","errorCode":null,"errorMessage":"the index response carried no readable body: ${url}","messagePattern":"the index response carried no readable body: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/h1b-sponsor/install-h1b-index.mjs","lineNumber":152,"sourceCode":"}\n\n/**\n * Stream the asset to `tmpFile`, hashing as it goes, and return the digest.\n *\n * Streamed rather than buffered: the body is millions of times the size of\n * anything else this plugin reads, and readBoundedText's 1 MiB ceiling exists\n * precisely because nothing on the API path should ever be this big. Hashing\n * during the write means the file is never read a second time to verify it.\n */\nasync function downloadAsset(fetchImpl, url, tmpFile) {\n  return fetchImpl(url, { timeoutMs: ASSET_TIMEOUT_MS }, async res => {\n    if (res.status !== 200) throw new Error(`could not download the index (HTTP ${res.status}): ${url}`);\n    const declared = Number(res.headers?.get?.('content-length'));\n    if (Number.isFinite(declared) && declared > MAX_INDEX_BYTES) {\n      throw new Error(`the published index exceeds ${MAX_INDEX_BYTES} bytes (${declared}): ${url}`);\n    }\n    if (!res.body || typeof res.body.getReader !== 'function') {\n      throw new Error(`the index response carried no readable body: ${url}`);\n    }\n\n    const hash = createHash('sha256');\n    const out = createWriteStream(tmpFile);\n    // A write failure (disk full, an unwritable target) arrives as an 'error'\n    // event on the stream, and an EventEmitter error with no listener is an\n    // uncaughtException: the CLI died on a stack trace instead of returning\n    // the envelope, and the cleanup that removes the partial .tmp file never\n    // ran. Recording the error here makes the crash impossible; the checks\n    // below turn it into the ordinary failure it is. once(out, 'drain') needs\n    // no extra wiring, it already rejects when 'error' fires mid-wait.\n    let writeError = null;\n    out.on('error', err => { writeError = err; });\n    const reader = res.body.getReader();\n    let total = 0;\n    try {\n      for (;;) {\n        if (writeError) {","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/plugins/h1b-sponsor/install-h1b-index.mjs#L134-L170","documentation":"downloadAsset() streams the response body via res.body.getReader(); if the 200 response carries no body, or a body that is not a readable stream (no getReader method), the download cannot be hashed or written and this error is thrown. This catches odd runtime environments and proxies that strip or replace bodies.","triggerScenarios":"The asset response is 200 with an acceptable Content-Length, but res.body is null/undefined or lacks a getReader function when downloadAsset inspects it.","commonSituations":"A custom fetchImpl was injected (tests, CI shim, proxy client) that returns responses without a Web ReadableStream body; a HEAD-like or empty response from a misbehaving proxy; a Node version/runtime where the fetch polyfill exposes a different body shape (e.g. a Buffer or Node stream instead of a web stream).","solutions":["Check what fetchImpl you pass to the installer — if custom, make it return a Response whose body is a Web ReadableStream.","Remove a proxy shim/interceptor that strips response bodies, or upgrade it to a fetch-compatible implementation.","Confirm your Node.js version has native fetch with web-stream bodies (Node 18+), or use a runtime-supported fetch.","Retry against the default endpoint with the built-in fetch to isolate the custom client as the cause."],"exampleFix":"// before\nconst fetchImpl = () => Promise.resolve({ status: 200, headers: new Headers(), body: Buffer.alloc(0) })\n// after\nconst fetchImpl = (url, opts, onRes) => fetch(url, opts).then(async res => ({ status: res.status, headers: res.headers, body: res.body, text: await onRes(res) }))","handlingStrategy":"type-guard","validationCode":"const res = await fetch(assetUrl);\nif (!res.body || typeof res.body.getReader !== 'function') {\n  throw new Error('your fetch client returns a response without a Web ReadableStream body; the installer cannot stream it');\n}","typeGuard":"function hasReadableBody(res) {\n  return !!res.body && typeof res.body.getReader === 'function';\n}","tryCatchPattern":"try {\n  await installH1BIndex();\n} catch (e) {\n  if (String(e.message).includes('no readable body')) {\n    console.error('Custom fetchImpl or runtime returns a non-stream body; use native fetch (Node 18+).');\n  } else throw e;\n}","preventionTips":["Use runtime-native fetch (Node 18+) rather than polyfills that expose Buffer/Node-stream bodies.","If you inject a custom fetchImpl, return real Response objects with web-stream bodies.","Remove proxy shims/interceptors that strip response bodies in CI.","Smoke-test the fetch client against a small download before wiring it into installs."],"tags":["network","streams","environment"],"backgroundTag":"missing-response-body","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}