JuliusBrussee/caveman · error · CaveRequestError
cave checkpoint response missing source_ref or messages
Error message
cave checkpoint response missing source_ref or messages
What it means
`trace.checkpoints.expand(sourceRef)` requires the expand endpoint to return a non-empty `source_ref` string and a `messages` array; both are mandatory because a checkpoint that cannot be expanded is treated as a bug. The library throws CaveRequestError when the response violates this contract.
Solutions
- Verify the checkpoint sourceRef is valid and was created by a compatible backend version
- Check backend version supports checkpoint expand returning source_ref + messages
- Catch CaveRequestError and handle the un-expandable checkpoint (rebuild conversation state)
Example fix
// before
const { messages } = await trace.checkpoints.expand(ref);
// after
let expanded;
try {
expanded = await trace.checkpoints.expand(ref);
} catch (e) {
if (e instanceof CaveRequestError && e.message.includes("source_ref")) {
return rebuildMessagesFromScratch();
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
const looksExpandable = (r) => typeof r?.source_ref === "string" && r.source_ref.trim() !== "" && Array.isArray(r.messages);
Type guard
function isCheckpointExpandResponse(r: unknown): r is { source_ref: string; messages: unknown[] } {
return typeof r === "object" && r !== null &&
typeof (r as any).source_ref === "string" && (r as any).source_ref.trim() !== "" &&
Array.isArray((r as any).messages);
} Try / catch
try {
return await trace.checkpoints.expand(ref);
} catch (e) {
if (e instanceof CaveRequestError && e.message.includes("source_ref")) {
return rebuildConversationState(ref);
}
throw e;
} Prevention
- Only expand checkpoints created by a compatible backend version
- Validate refs before expanding (format, origin trace)
- Add an integration test covering the expand round-trip
When it happens
Trigger: Calling expand with a sourceRef whose server response lacks/empties source_ref or omits messages (not an array).
Common situations: Backend version mismatch with an older expand endpoint; corrupted or foreign checkpoint reference; proxy altering the response body.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- cave artifact response missing artifact_id
- artifactId is required
- assemble requires model and sessionId
- cave_harness_adapter_version_invalid
- cave_harness_artifact_digest_invalid
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/6c1cd721f8641962.
Report an issue: GitHub.
Appendix: source
Thrown at packages/sdk/typescript/src/index.ts:860
return `[cave-artifact id=${response.artifact_id} source=${options.source} type=${options.contentType ?? "application/json"}]\nsummary: artifact stored.\nretrieve: authenticated GET /sdk/v1/artifacts/${response.artifact_id} or trace.artifacts.get("${response.artifact_id}").\n[/cave-artifact]`;
},
get: async (artifactId: string): Promise<unknown> => artifactGet(this.cave, artifactId, this.workflow, this.traceContext())
};
context = {
checkpoint: async (messages: unknown[], options: Record<string, unknown>) => request(this.cave, "/sdk/v1/checkpoints", { messages, options, workflow: this.workflow }, this.workflow, this.traceContext()),
/**
* Reverse a checkpoint `source_ref` back into the original context.
*
* GETs `/sdk/v1/checkpoints/{sourceRef}/expand`; the gateway returns the
* stored `{ source_ref, version, messages, checkpoint }`. This is the other
* half of `checkpoint()` — reversibility is mandatory: a checkpoint that
* cannot be expanded is a bug.
*/
expand: async (sourceRef: string) => {
const response = await request(this.cave, `/sdk/v1/checkpoints/${encodeURIComponent(sourceRef)}/expand`, undefined, this.workflow, this.traceContext());
if (typeof response.source_ref !== "string" || response.source_ref.trim() === "" || !Array.isArray(response.messages)) {
throw new CaveRequestError(200, "/sdk/v1/checkpoints/{sourceRef}/expand", "cave checkpoint response missing source_ref or messages");
}
return response;
}
};
}
// ─── Runtime policy client ────────────────────────────────────────────────────
/** Schema version this client accepts; anything else is rejected (fail closed). */
const RUNTIME_POLICY_SCHEMA_VERSION = "caveman.runtime-policy.v1";
/** Runtime-policy envelopes are KB-sized; stop a hostile body after 1 MiB. */
const RUNTIME_POLICY_MAX_RESPONSE_BYTES = 1024 * 1024;
async function readRuntimePolicyResponseText(response: Response): Promise<string> {
const contentLength = response.headers?.get?.("content-length");
if (typeof contentLength === "string" && /^[0-9]+$/.test(contentLength) && Number(contentLength) > RUNTIME_POLICY_MAX_RESPONSE_BYTES) {
await Promise.resolve(response.body?.cancel?.()).catch(() => undefined);View on GitHub (pinned to 3ee70a1026)