JuliusBrussee/caveman · error · Error
cave_harness_request_invalid
cave_harness_request_invalid
Error message
cave_harness_request_invalid
What it means
Inside `adapter.run`, after snapshotting the request and preparing locked execution, the frozen request must carry a `prompt` that is a string of length > 0 and a `runID` that is a string of length > 0; otherwise `cave_harness_request_invalid` throws. The same code is thrown by `snapshotRequest` when the request cannot be `structuredClone`d (unserializable fields) — only a real `AbortSignal` is exempted from cloning. It fires before any provider traffic, so nothing is billed.
Solutions
- Generate the run ID explicitly (e.g. `crypto.randomUUID()`) before calling `run`
- Validate `typeof prompt === 'string' && prompt.length > 0` at the call site
- Keep the request a plain serializable object; put only the AbortSignal outside cloneable data
Example fix
// before
await adapter.run({ ...base, prompt: userPrompt?.trim(), runID: session.lastRunID ?? '' })
// after
const runID = session.lastRunID ?? crypto.randomUUID();
if (!userPrompt?.trim()) throw new Error('empty prompt');
await adapter.run({ ...base, prompt: userPrompt, runID }); Defensive patterns
Strategy: validation
Validate before calling
function isRunnableRequest(r: Partial<HarnessRequest>): r is HarnessRequest {
return typeof r.prompt === 'string' && r.prompt.length > 0 &&
typeof r.runID === 'string' && r.runID.length > 0;
}
if (!isRunnableRequest(req)) throw new Error('harness request needs a non-empty prompt and runID');
// keep the request plain-cloneable: no class instances, functions, or DOM nodes
await adapter.run(req); Type guard
function isRunnableRequest(r: Partial<HarnessRequest>): r is HarnessRequest {
return typeof r.prompt === 'string' && r.prompt.length > 0 &&
typeof r.runID === 'string' && r.runID.length > 0;
} Try / catch
try {
return await adapter.run(req);
} catch (e) {
if (e instanceof Error && e.message === 'cave_harness_request_invalid') {
// nothing was sent or billed — fix the caller's request shape (prompt/runID)
// or remove non-cloneable fields, then rebuild the request
}
throw e;
} Prevention
- Generate runID with crypto.randomUUID() in the layer that builds the request
- Reject empty prompts at the API boundary before they reach the adapter
- Keep request payloads structuredClone-safe; only the AbortSignal is exempt
When it happens
Trigger: `adapter.run({ ...base, runID: "" })`; `prompt: undefined` from an optional chain that resolved empty; a request object containing class instances, functions, or DOM nodes that fail structuredClone.
Common situations: Forgetting to thread a generated run ID from the orchestration layer; building the prompt from an empty template or an empty user input; test harnesses reusing a mutable request object that got mutated between runs.
Related errors
- cave_harness_adapter_version_invalid
- cave_harness_upstream_version_invalid
- artifactId is required
- assemble requires model and sessionId
- cave artifact response missing artifact_id
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/02c6433bd3ec28ed.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/adapters.ts:127
return Object.freeze({
id,
version: identity.adapterVersion,
manifest,
contractSHA256,
async run(request: HarnessRequest): Promise<HarnessResult> {
const frozenRequest = snapshotRequest(request);
const prepared = prepareLockedHarnessExecution({
build: frozenRequest.build,
harness: id,
adapterVersion: identity.adapterVersion,
upstreamVersion: identity.upstreamVersion,
contextIR: frozenRequest.contextIR,
plan: frozenRequest.plan,
});
const { build, planSHA256, contextIRSHA256 } = prepared;
if (typeof frozenRequest.prompt !== "string" || frozenRequest.prompt.length === 0 ||
typeof frozenRequest.runID !== "string" || frozenRequest.runID.length === 0) {
throw new Error("cave_harness_request_invalid");
}
if (isAborted(frozenRequest.signal)) throw new Error("cave_harness_aborted");
validateTransformEvidence(frozenRequest, frozenRequest.plan);
const execution = snapshotExecution(await invoke(frozenRequest));
if (isAborted(frozenRequest.signal)) throw new Error("cave_harness_aborted");
validateExecution(execution, frozenRequest.plan);
return {
terminal: execution.terminal,
text: execution.text,
provider: execution.provider,
model: execution.model,
inputTokens: execution.inputTokens,
outputTokens: execution.outputTokens,
cacheReadTokens: execution.cacheReadTokens,
cacheWriteTokens: execution.cacheWriteTokens,
reasoningTokens: execution.reasoningTokens,
totalTokens: execution.totalTokens,
costUsd: execution.costUsd,View on GitHub (pinned to 766dce6b13)