{"record":{"id":"b515fb36e7757e58","repo":"JuliusBrussee/caveman","slug":"cave-artifact-was-not-valid-json","errorCode":null,"errorMessage":"cave artifact was not valid JSON","messagePattern":"cave artifact was not valid JSON","errorType":"exception","errorClass":"CaveRequestError","httpStatus":null,"severity":"error","filePath":"packages/sdk/typescript/src/index.ts","lineNumber":2087,"sourceCode":"  }\n  if (decoded === null || typeof decoded !== \"object\" || Array.isArray(decoded)) {\n    throw new CaveRequestError(response.status, path, \"cave response must be a JSON object\");\n  }\n  return decoded as Record<string, unknown>;\n}\n\nasync function artifactGet(cave: Cave, artifactId: string, workflow: string, trace: TraceContext): Promise<unknown> {\n  if (typeof artifactId !== \"string\" || artifactId.trim() === \"\") throw new Error(\"artifactId is required\");\n  const path = `/sdk/v1/artifacts/${encodeURIComponent(artifactId)}`;\n  const response = await caveFetch(cave, `${cave.options.baseURL}${path}`, {\n    method: \"GET\",\n    headers: headers(cave, workflow, undefined, undefined, trace)\n  });\n  if (!response.ok) throw new CaveRequestError(response.status, path, `cave request failed (${response.status})`);\n  try {\n    return JSON.parse(await response.text());\n  } catch {\n    throw new CaveRequestError(response.status, path, \"cave artifact was not valid JSON\");\n  }\n}\n\nfunction headers(cave: Cave, workflow: string, upstreamKey?: string, hint?: Record<string, unknown>, trace?: TraceContext) {\n  return {\n    \"content-type\": \"application/json\",\n    authorization: `Bearer ${cave.options.apiKey}`,\n    \"x-cave-agent\": cave.options.agent,\n    \"x-cave-workflow\": workflow,\n    \"x-cave-retention\": cave.options.retention ?? \"metadata\",\n    ...(cave.options.user ? { \"x-cave-user-hash\": cave.options.user } : {}),\n    ...(upstreamKey ? { \"x-cave-upstream-key\": upstreamKey } : {}),\n    ...(hint?.[\"latencyClass\"] !== undefined ? { \"x-cave-async\": String(hint[\"latencyClass\"] !== \"interactive\") } : {}),\n    ...(typeof hint?.[\"toolSessionId\"] === \"string\" ? { \"x-cave-tool-session\": hint[\"toolSessionId\"] as string } : {}),\n    ...(typeof hint?.[\"assemblyHeader\"] === \"string\" ? { \"x-cave-assembly\": hint[\"assemblyHeader\"] as string } : {}),\n    // Trace continuity: only requests made through a CaveTrace carry these, so\n    // the gateway row's span parents onto the SDK's root span.\n    ...(trace ? { \"x-cave-trace-id\": trace.traceId, \"x-cave-parent-span-id\": trace.parentSpanId } : {})","sourceCodeStart":2069,"sourceCodeEnd":2105,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/sdk/typescript/src/index.ts#L2069-L2105","documentation":"A CaveRequestError thrown by artifactGet() when the gateway responded 2xx but JSON.parse(response.text()) failed — the artifact body is not valid JSON. The status and path are attached so the failure is attributable even though the server claimed success.","triggerScenarios":"A corporate proxy or captive portal injecting an HTML page with status 200; truncated response body from a dropped connection; content-encoding corruption; a gateway bug returning an empty body for an artifact.","commonSituations":"MITM proxies rewriting responses; flaky mobile or hotel networks truncating bodies; gateway version skew where the artifact route returns non-JSON on partial storage failure.","solutions":["Retry once — truncated or mangled bodies are usually transient","Inspect the raw response by curling the same URL with the same headers to see what the body actually is","If a proxy is involved, bypass it or add the gateway host to its exception list","If reproducible against the real gateway, capture the body and report it as a server bug"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"let last;\nfor (let attempt = 0; attempt < 2; attempt++) {\n  try { return await trace.artifacts.get(id); }\n  catch (e) {\n    last = e;\n    if (!(e instanceof CaveRequestError) || !/not valid JSON/.test(e.message)) throw e;\n  }\n}\nthrow last; // one retry only — mangled bodies are usually transient","preventionTips":["Treat any 2xx-but-unparseable artifact body as transient first, persistent second (then it is a gateway or proxy bug to report)","Keep proxies off the gateway host if you see this repeatedly"],"tags":["json","network","sdk","artifacts"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}