Hmbown/CodeWhale · error

Missing release response

Error message

Missing release response

What it means

boundedJson streams a Response body up to a size limit and JSON-parses it; it throws "Missing release response" immediately when response.body is null. This happens when the runtime hands back a body-less Response (e.g. certain redirects, opaque responses, or platform edge cases), so the release/receipt fetchers cannot read release data at all.

Solutions

  1. Check response.ok and response.body before passing to boundedJson and fail with a clearer error naming the URL.
  2. Create a fresh fetch for each call — never reuse a Response whose body may already be consumed.
  3. If the upstream is flaky, retry the fetch once before giving up.
  4. In tests, construct the Response with an explicit body: new Response(JSON.stringify(data), { status: 200 }).

Example fix

// before
const json = await boundedJson(res, LIMIT);

// after
if (!res.ok || !res.body) throw new Error(`release fetch failed: ${res.status}`);
const json = await boundedJson(res, LIMIT);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!res.ok || !res.body) throw new Error(`release fetch failed for ${url}: status=${res.status}`);

Type guard

const hasBody = (res) => res instanceof Response && res.body !== null;

Try / catch

try {
  const data = await boundedJson(res, LIMIT);
} catch (e) {
  if (e.message === 'Missing release response') {
    // retry once with a fresh fetch, then surface a clear error
    const fresh = await fetch(url, { redirect: 'follow' });
    return getComputerUseRelease(...);
  }
  throw e;
}

Prevention

When it happens

Trigger: A fetch of the release manifest/asset returns a Response whose body is null — typically from a network stack quirk, an already-consumed response, or a redirect the runtime did not follow with a body.

Common situations: Cloudflare Workers fetch returning a null body for certain upstream errors; passing a synthetic Response created without a body in tests; calling the fetcher twice and reusing a consumed response.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/fccacbfa437c0c74. Report an issue: GitHub.

Appendix: source

Thrown at web/lib/computer-use-release.ts:92

  if (receipt.version !== version || receipt.archive !== archive || receipt.platform !== "macos"
    || receipt.arch !== "universal" || receipt.notarized !== true
    || receipt.sha256 !== sha256 || receipt.size !== zip.size) return { status: "pending" };
  // The image is offered only when the receipt and GitHub's digest agree on it; otherwise the archive alone is offered.
  const image = receiptImage(receipt, version);
  const dmg = assets.dmg && image && (assets.dmg.digest as string).slice(7) === image.sha256 && assets.dmg.size === image.size
    ? { downloadUrl: assets.dmg.browser_download_url as string, size: image.size, sha256: image.sha256 } : undefined;
  return {
    status: "ready", version, sha256, size: zip.size as number,
    url: `${COMPUTER_USE_REPO}/releases/tag/v${version}`,
    downloadUrl: zip.browser_download_url as string,
    receiptUrl: assets.receipt.browser_download_url as string,
    verification: "github-digest",
    ...(dmg ? { dmg } : {}),
  };
}

async function boundedJson(response: Response, limit: number): Promise<unknown> {
  if (!response.body) throw new Error("Missing release response");
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let length = 0, text = "";
  try {
    for (;;) {
      const { done, value } = await reader.read();
      if (done) break;
      length += value.byteLength;
      if (length > limit) throw new Error("Release response exceeds size limit");
      text += decoder.decode(value, { stream: true });
    }
    return JSON.parse(text + decoder.decode());
  } finally { await reader.cancel(); reader.releaseLock(); }
}

const WEB_HEADERS = { "User-Agent": "codewhale-web" };

/** GET a release web endpoint, following at most three 302s and only onto GitHub's release hosts over https. */

View on GitHub (pinned to 433685b202)