santifer/career-ops · error · Error

could not download the index (HTTP ${res.status}): ${url}

Error message

could not download the index (HTTP ${res.status}): ${url}

What it means

downloadAsset() fetches the index asset and requires HTTP 200. Any other status (404, 403, 5xx) aborts the download with this error including the status code and URL. The pointer said an asset should exist at this URL; a non-200 means the release is inconsistent or the endpoint is misbehaving.

Source

Thrown at plugins/h1b-sponsor/install-h1b-index.mjs:146

  // Recorded, never acted on, so a missing or odd value costs a label rather
  // than the install. Bounded because it lands in a file on disk.
  const version = doc.version === undefined || doc.version === null
    ? null
    : String(doc.version).slice(0, 64);
  return { filename, sha256, version };
}

/**
 * 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;

View on GitHub (pinned to 1696bec4d0)

Solutions

  1. Retry after a short delay — a freshly published pointer can precede the asset upload, causing a transient 404.
  2. curl the asset URL to see the actual status/body; a 403/404 body often names the cause (auth, deleted object).
  3. Verify H1B_API_BASE so the asset URL resolves on the host that actually stores the index.
  4. If the pointer is permanently ahead of the assets, wait for the release to be fixed or pin an earlier pointer.

Example fix

// before
H1B_API_BASE=https://wrong-host.example.com   // asset URL 404s
// after
unset H1B_API_BASE  // use the default endpoint where the index asset exists
Defensive patterns

Strategy: retry

Validate before calling

const res = await fetch(assetUrl, { method: 'HEAD' });
if (!res.ok) console.warn(`asset currently ${res.status} at ${assetUrl}; retry shortly or check the release`);

Try / catch

try {
  await installH1BIndex();
} catch (e) {
  if (String(e.message).includes('could not download the index (HTTP')) {
    const status = e.message.match(/HTTP (\d+)/)?.[1];
    if (status === '404' || status === '5xx') await sleep(30_000).then(retry); // pointer may precede asset upload
    else throw e;
  } else throw e;
}

Prevention

When it happens

Trigger: downloadAsset(fetchImpl, url, tmpFile) invokes fetchImpl and the response handler sees res.status !== 200 for the index asset URL derived from the pointer's filename.

Common situations: The pointer was updated to a new filename before the asset finished uploading (transient 404); the asset was deleted or the release rolled back (404); a private bucket rejects anonymous download (403); CDN or origin returns 5xx; an incorrect H1B_API_BASE builds asset URLs on the wrong host.

Related errors


AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01). Data as JSON: /api/errors/10dd5d4666ed4f6e. Report an issue: GitHub.