musistudio/claude-code-router · error
The CCR artifact response was empty.
Error message
The CCR artifact response was empty.
What it means
The artifact response body was consumed successfully but contained zero bytes (total === 0). An empty body cannot be a valid image or video, so it is rejected before MIME sniffing.
Source
Thrown at packages/core/src/agents/codex/media-preview-bridge.ts:332
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);
if (!detectedMimeType || mediaKind(detectedMimeType) !== declaredKind) {
throw new Error("The CCR artifact content did not match its declared media type.");
}
return { bytes, mimeType: detectedMimeType };
}
function detectMediaMimeType(buffer: Buffer): string | undefined {
if (buffer.byteLength >= 8 && buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) return "image/png";
if (buffer.byteLength >= 3 && buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) return "image/jpeg";
if (buffer.byteLength >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") return "image/webp";
if (buffer.byteLength >= 12 && buffer.subarray(4, 8).toString("ascii") === "ftyp") {
const brand = buffer.subarray(8, 12).toString("ascii");
if (["avif", "avis", "mif1", "msf1"].includes(brand)) return "image/avif";
return "video/mp4";
}View on GitHub (pinned to 99f24806c6)
Solutions
- curl the artifact URL and confirm whether the body is genuinely empty server-side
- If the artifact object is empty in storage, re-upload/re-generate it on the CCR side
- In tests, ensure mocked responses include real media bytes
Example fix
// before
global.fetch = async () => new Response(new Uint8Array(0), { headers: { 'content-type': 'image/png' } });
// after
global.fetch = async () => new Response(pngBytes, { headers: { 'content-type': 'image/png' } }); Defensive patterns
Strategy: try-catch
Validate before calling
async function artifactHasBody(url: string): Promise<boolean> {
const res = await fetch(url, { method: 'HEAD' });
return Number(res.headers.get('content-length') || '0') !== 0;
} Type guard
function isEmptyArtifactRejection(e: unknown): boolean {
return e instanceof Error && e.message.includes('response was empty');
} Try / catch
try {
const media = await bridge.artifact(ref);
} catch (e) {
if (isEmptyArtifactRejection(e)) return renderPlaceholder(ref);
throw e;
} Prevention
- Verify artifact uploads completed before referencing them
- Include real bytes in fetch mocks
- Treat empty artifacts as corrupt data upstream, not as preview failures
When it happens
Trigger: Server returns 200 with correct Content-Type but an empty body (Content-Length: 0 or an immediately-ending stream); mocks returning empty Responses.
Common situations: Upstream bug writing an empty artifact; storage race where the artifact record exists but the object upload failed; test mocks with empty buffers.
Related errors
- The CCR artifact endpoint returned a non-media content type.
- The CCR media artifact exceeds the inline preview size limit
- Compressed CCR media artifacts are not accepted for inline p
- The CCR artifact content did not match its declared media ty
- duration is required for ${target.protocol}.
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/49a25f35fa22000c.
Report an issue: GitHub.