JuliusBrussee/caveman · error · CaveRequestError
cave artifact was not valid JSON
Error message
cave artifact was not valid JSON
What it means
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.
Source
Thrown at packages/sdk/typescript/src/index.ts:2087
}
if (decoded === null || typeof decoded !== "object" || Array.isArray(decoded)) {
throw new CaveRequestError(response.status, path, "cave response must be a JSON object");
}
return decoded as Record<string, unknown>;
}
async function artifactGet(cave: Cave, artifactId: string, workflow: string, trace: TraceContext): Promise<unknown> {
if (typeof artifactId !== "string" || artifactId.trim() === "") throw new Error("artifactId is required");
const path = `/sdk/v1/artifacts/${encodeURIComponent(artifactId)}`;
const response = await caveFetch(cave, `${cave.options.baseURL}${path}`, {
method: "GET",
headers: headers(cave, workflow, undefined, undefined, trace)
});
if (!response.ok) throw new CaveRequestError(response.status, path, `cave request failed (${response.status})`);
try {
return JSON.parse(await response.text());
} catch {
throw new CaveRequestError(response.status, path, "cave artifact was not valid JSON");
}
}
function headers(cave: Cave, workflow: string, upstreamKey?: string, hint?: Record<string, unknown>, trace?: TraceContext) {
return {
"content-type": "application/json",
authorization: `Bearer ${cave.options.apiKey}`,
"x-cave-agent": cave.options.agent,
"x-cave-workflow": workflow,
"x-cave-retention": cave.options.retention ?? "metadata",
...(cave.options.user ? { "x-cave-user-hash": cave.options.user } : {}),
...(upstreamKey ? { "x-cave-upstream-key": upstreamKey } : {}),
...(hint?.["latencyClass"] !== undefined ? { "x-cave-async": String(hint["latencyClass"] !== "interactive") } : {}),
...(typeof hint?.["toolSessionId"] === "string" ? { "x-cave-tool-session": hint["toolSessionId"] as string } : {}),
...(typeof hint?.["assemblyHeader"] === "string" ? { "x-cave-assembly": hint["assemblyHeader"] as string } : {}),
// Trace continuity: only requests made through a CaveTrace carry these, so
// the gateway row's span parents onto the SDK's root span.
...(trace ? { "x-cave-trace-id": trace.traceId, "x-cave-parent-span-id": trace.parentSpanId } : {})View on GitHub (pinned to 27d5a3981a)
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
Defensive patterns
Strategy: retry
Try / catch
let last;
for (let attempt = 0; attempt < 2; attempt++) {
try { return await trace.artifacts.get(id); }
catch (e) {
last = e;
if (!(e instanceof CaveRequestError) || !/not valid JSON/.test(e.message)) throw e;
}
}
throw last; // one retry only — mangled bodies are usually transient Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- artifactId is required
- secretbox: payload KMS encrypt: %w
- cave_eve_terminal_${result.status}
- cave_harness_upstream_version_mismatch
- cave_mastra_max_steps_invalid
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/b515fb36e7757e58.
Report an issue: GitHub.