musistudio/claude-code-router · error

The CCR artifact response had no body.

Error message

The CCR artifact response had no body.

What it means

The HTTP response succeeded and passed all header checks but response.body is null, so there is no stream to read. This happens with unusual fetch implementations or synthetic Response objects that carry headers without a body.

Source

Thrown at packages/core/src/agents/codex/media-preview-bridge.ts:317

      signal
    });
  } catch {
    throw new Error("The CCR artifact request failed.");
  }
  if (!response.ok) throw new Error(`The CCR artifact endpoint returned HTTP ${response.status}.`);
  if (response.redirected) throw new Error("The CCR artifact endpoint attempted a redirect.");
  const declaredMimeType = (response.headers.get("content-type") || "").split(";", 1)[0].trim().toLowerCase();
  const declaredKind = mediaKind(declaredMimeType);
  if (!declaredKind) throw new Error("The CCR artifact endpoint returned a non-media content type.");
  const maxBytes = declaredKind === "video" ? codexMediaPreviewMaxVideoBytes : codexMediaPreviewMaxImageBytes;
  const declaredLength = Number(response.headers.get("content-length") || "0");
  if (declaredLength && (!Number.isSafeInteger(declaredLength) || declaredLength < 1 || declaredLength > maxBytes)) {
    throw new Error("The CCR media artifact exceeds the inline preview size limit.");
  }
  if (response.headers.get("content-encoding") && response.headers.get("content-encoding") !== "identity") {
    throw new Error("Compressed CCR media artifacts are not accepted for inline preview.");
  }
  if (!response.body) throw new Error("The CCR artifact response had no body.");
  const reader = response.body.getReader();
  const chunks: Buffer[] = [];
  let total = 0;
  while (true) {
    const part = await reader.read();
    if (part.done) break;
    if (!part.value?.byteLength) continue;
    total += part.value.byteLength;
    if (total > maxBytes) {
      await reader.cancel();
      throw new Error("The CCR media artifact exceeds the inline preview size limit.");
    }
    chunks.push(Buffer.from(part.value));
  }
  if (!total) throw new Error("The CCR artifact response was empty.");
  if (declaredLength && total !== declaredLength) throw new Error("The CCR artifact response length did not match its headers.");
  const bytes = Buffer.concat(chunks, total);
  const detectedMimeType = detectMediaMimeType(bytes);

View on GitHub (pinned to 99f24806c6)

Solutions

  1. If testing, make the mocked Response include a real body (new Response(bytes) or a ReadableStream)
  2. Check the actual raw exchange with curl to confirm the server sends a body
  3. Ensure the runtime provides spec-compliant fetch with streaming bodies (Node >= 18 undici)

Example fix

// before (test stub without body)
global.fetch = async () => new Response(null, { headers }); // triggers error

// after
global.fetch = async () => new Response(bytes, { headers });
Defensive patterns

Strategy: try-catch

Type guard

function isNoBodyRejection(e: unknown): boolean {
  return e instanceof Error && e.message.includes('had no body');
}

Try / catch

try {
  const media = await bridge.artifact(ref);
} catch (e) {
  if (isNoBodyRejection(e)) return renderPlaceholder(ref);
  throw e;
}

Prevention

When it happens

Trigger: A fetch polyfill or mock returns a Response with body === null (e.g. constructed via new Response() without a body in tests); a HEAD-like response from a misbehaving server or interceptor; runtimes where streaming bodies aren't supported.

Common situations: Unit tests stubbing fetch with minimal Response objects; HTTP/2 or proxy middleware that strips the body; calling the endpoint with methods that yield no body.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/5b5ec18484c0ffc5. Report an issue: GitHub.