santifer/career-ops · error · Error
the index response carried no readable body: ${url}
Error message
the index response carried no readable body: ${url} What it means
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.
Source
Thrown at plugins/h1b-sponsor/install-h1b-index.mjs:152
}
/**
* Stream the asset to `tmpFile`, hashing as it goes, and return the digest.
*
* Streamed rather than buffered: the body is millions of times the size of
* anything else this plugin reads, and readBoundedText's 1 MiB ceiling exists
* precisely because nothing on the API path should ever be this big. Hashing
* during the write means the file is never read a second time to verify it.
*/
async function downloadAsset(fetchImpl, url, tmpFile) {
return fetchImpl(url, { timeoutMs: ASSET_TIMEOUT_MS }, async res => {
if (res.status !== 200) throw new Error(`could not download the index (HTTP ${res.status}): ${url}`);
const declared = Number(res.headers?.get?.('content-length'));
if (Number.isFinite(declared) && declared > MAX_INDEX_BYTES) {
throw new Error(`the published index exceeds ${MAX_INDEX_BYTES} bytes (${declared}): ${url}`);
}
if (!res.body || typeof res.body.getReader !== 'function') {
throw new Error(`the index response carried no readable body: ${url}`);
}
const hash = createHash('sha256');
const out = createWriteStream(tmpFile);
// A write failure (disk full, an unwritable target) arrives as an 'error'
// event on the stream, and an EventEmitter error with no listener is an
// uncaughtException: the CLI died on a stack trace instead of returning
// the envelope, and the cleanup that removes the partial .tmp file never
// ran. Recording the error here makes the crash impossible; the checks
// below turn it into the ordinary failure it is. once(out, 'drain') needs
// no extra wiring, it already rejects when 'error' fires mid-wait.
let writeError = null;
out.on('error', err => { writeError = err; });
const reader = res.body.getReader();
let total = 0;
try {
for (;;) {
if (writeError) {View on GitHub (pinned to 1696bec4d0)
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.
Example fix
// before
const fetchImpl = () => Promise.resolve({ status: 200, headers: new Headers(), body: Buffer.alloc(0) })
// after
const fetchImpl = (url, opts, onRes) => fetch(url, opts).then(async res => ({ status: res.status, headers: res.headers, body: res.body, text: await onRes(res) })) Defensive patterns
Strategy: type-guard
Validate before calling
const res = await fetch(assetUrl);
if (!res.body || typeof res.body.getReader !== 'function') {
throw new Error('your fetch client returns a response without a Web ReadableStream body; the installer cannot stream it');
} Type guard
function hasReadableBody(res) {
return !!res.body && typeof res.body.getReader === 'function';
} Try / catch
try {
await installH1BIndex();
} catch (e) {
if (String(e.message).includes('no readable body')) {
console.error('Custom fetchImpl or runtime returns a non-stream body; use native fetch (Node 18+).');
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- the published index exceeds ${MAX_INDEX_BYTES} bytes: ${url}
- refusing to archive restricted destination: ${preGuard.reaso
- refusing to archive restricted destination after redirect: $
- Invalid or blocked URL: ${rejected.reason}
- openai-eval.mjs exited ${res.status}: ${(res.stderr || '').s
AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01).
Data as JSON: /api/errors/3a9074c9dbea0301.
Report an issue: GitHub.