{"record":{"id":"c5610d9e619dcf66","repo":"bytedance/deer-flow","slug":"failed-to-load-artifact-response-status","errorCode":null,"errorMessage":"Failed to load artifact: ${response.status}","messagePattern":"Failed to load artifact: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/core/artifacts/loader.ts","lineNumber":63,"sourceCode":"  const response = await fetch(url, {\n    cache: \"no-store\",\n    headers: full\n      ? undefined\n      : { Range: `bytes=0-${ARTIFACT_PREVIEW_MAX_BYTES - 1}` },\n  });\n  const contentRange = parseContentRange(response.headers.get(\"Content-Range\"));\n  if (response.status === 416 && contentRange?.total === 0) {\n    return {\n      content: \"\",\n      url,\n      truncated: false,\n      previewBytes: 0,\n      totalBytes: 0,\n      sha256: await sha256OfText(\"\"),\n    };\n  }\n  if (!response.ok) {\n    throw new Error(`Failed to load artifact: ${response.status}`);\n  }\n\n  const bytes = await response.arrayBuffer();\n  const truncated =\n    !full &&\n    response.status === 206 &&\n    (contentRange?.end === undefined ||\n      contentRange.total > contentRange.end + 1);\n  // Streaming decode intentionally holds an incomplete trailing UTF-8 code\n  // point instead of fabricating U+FFFD at the range boundary.\n  const content = new TextDecoder().decode(bytes, { stream: truncated });\n  const etag = response.headers.get(\"etag\");\n  const sha256 =\n    etag?.match(/^\"([0-9a-f]{64})\"$/)?.[1] ??\n    (!truncated ? await sha256OfText(content) : undefined);\n  const contentLengthHeader = response.headers.get(\"Content-Length\");\n  const contentLength =\n    contentLengthHeader === null ? undefined : Number(contentLengthHeader);","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/frontend/src/core/artifacts/loader.ts#L45-L81","documentation":"Thrown when loading artifact content via range-GET fails and the status is not the special empty-file case (416 with total 0). The loader uses Range requests for previews, so 416 with a non-zero total (range past EOF after truncation) and 5xx from the Gateway both land here. The error only embeds the numeric status.","triggerScenarios":"Requesting a range beyond the artifact's current size (file shrank between HEAD and GET); artifact deleted mid-preview (404); Gateway/nginx misconfig dropping Range support so a 200-with-Range mismatch confuses downstream parsing; proxy 502 during Gateway restart.","commonSituations":"Previewing an artifact that an agent turn is actively rewriting; nginx serving stale cached metadata; opening an old link to a pruned thread's artifact.","solutions":["Retry with full=true (no Range header) once — bypasses range-past-EOF 416s","On 404, re-check the artifact list for the thread and drop dead entries from the UI","Verify nginx forwards Range headers (proxy_set_header Range / proxy_pass behavior) if 200-for-206 symptoms appear","Re-request from offset 0 with the current total to resync after truncation"],"exampleFix":"// before\nconst page = await loadArtifact(url, {full: false});\n\n// after\ntry {\n  const page = await loadArtifact(url, {full: false});\n} catch (e) {\n  if (e instanceof Error && e.message.includes(': 416')) {\n    return loadArtifact(url, {full: true}); // resync after size change\n  }\n  throw e;\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":"export function isArtifactLoadError(e: unknown, status?: number): boolean {\n  return e instanceof Error && /^Failed to load artifact: \\d+$/.test(e.message);\n}","tryCatchPattern":"try {\n  return await loadArtifact(url, {full: false});\n} catch (e) {\n  if (isArtifactLoadError(e) && e.message.endsWith(': 416')) {\n    return loadArtifact(url, {full: true}); // range desync: refetch whole\n  }\n  if (isArtifactLoadError(e) && e.message.endsWith(': 404')) {\n    return null; // artifact gone\n  }\n  throw e;\n}","preventionTips":["Refetch preview from offset 0 after artifact size changes","Handle 416-with-total-0 empty-file case separately (loader already does)","Verify proxy passes Range headers when self-hosting nginx"],"tags":["artifacts","range-request","http","frontend"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}