{"record":{"id":"5b5ec18484c0ffc5","repo":"musistudio/claude-code-router","slug":"the-ccr-artifact-response-had-no-body","errorCode":null,"errorMessage":"The CCR artifact response had no body.","messagePattern":"The CCR artifact response had no body\\.","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/agents/codex/media-preview-bridge.ts","lineNumber":317,"sourceCode":"      signal\n    });\n  } catch {\n    throw new Error(\"The CCR artifact request failed.\");\n  }\n  if (!response.ok) throw new Error(`The CCR artifact endpoint returned HTTP ${response.status}.`);\n  if (response.redirected) throw new Error(\"The CCR artifact endpoint attempted a redirect.\");\n  const declaredMimeType = (response.headers.get(\"content-type\") || \"\").split(\";\", 1)[0].trim().toLowerCase();\n  const declaredKind = mediaKind(declaredMimeType);\n  if (!declaredKind) throw new Error(\"The CCR artifact endpoint returned a non-media content type.\");\n  const maxBytes = declaredKind === \"video\" ? codexMediaPreviewMaxVideoBytes : codexMediaPreviewMaxImageBytes;\n  const declaredLength = Number(response.headers.get(\"content-length\") || \"0\");\n  if (declaredLength && (!Number.isSafeInteger(declaredLength) || declaredLength < 1 || declaredLength > maxBytes)) {\n    throw new Error(\"The CCR media artifact exceeds the inline preview size limit.\");\n  }\n  if (response.headers.get(\"content-encoding\") && response.headers.get(\"content-encoding\") !== \"identity\") {\n    throw new Error(\"Compressed CCR media artifacts are not accepted for inline preview.\");\n  }\n  if (!response.body) throw new Error(\"The CCR artifact response had no body.\");\n  const reader = response.body.getReader();\n  const chunks: Buffer[] = [];\n  let total = 0;\n  while (true) {\n    const part = await reader.read();\n    if (part.done) break;\n    if (!part.value?.byteLength) continue;\n    total += part.value.byteLength;\n    if (total > maxBytes) {\n      await reader.cancel();\n      throw new Error(\"The CCR media artifact exceeds the inline preview size limit.\");\n    }\n    chunks.push(Buffer.from(part.value));\n  }\n  if (!total) throw new Error(\"The CCR artifact response was empty.\");\n  if (declaredLength && total !== declaredLength) throw new Error(\"The CCR artifact response length did not match its headers.\");\n  const bytes = Buffer.concat(chunks, total);\n  const detectedMimeType = detectMediaMimeType(bytes);","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/agents/codex/media-preview-bridge.ts#L299-L335","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["If testing, make the mocked Response include a real body (new Response(bytes) or a ReadableStream)","Check the actual raw exchange with curl to confirm the server sends a body","Ensure the runtime provides spec-compliant fetch with streaming bodies (Node >= 18 undici)"],"exampleFix":"// before (test stub without body)\nglobal.fetch = async () => new Response(null, { headers }); // triggers error\n\n// after\nglobal.fetch = async () => new Response(bytes, { headers });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isNoBodyRejection(e: unknown): boolean {\n  return e instanceof Error && e.message.includes('had no body');\n}","tryCatchPattern":"try {\n  const media = await bridge.artifact(ref);\n} catch (e) {\n  if (isNoBodyRejection(e)) return renderPlaceholder(ref);\n  throw e;\n}","preventionTips":["In tests, mock fetch with Responses that include real bodies","Use spec-compliant fetch runtimes (Node >= 18)","Avoid interceptors that consume or strip response bodies"],"tags":["fetch","response-body","streaming","testing"],"backgroundTag":"empty-response-body","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}