JuliusBrussee/caveman · error · CavemanRunError
cave_claude_terminal_missing
cave_claude_terminal_missing
Error message
the SDK reported no accountable usage on a successful result
What it means
On a successful SDK result the executor must produce accountable usage (token counts that pass `usageFromSDKResult`/`validateProviderUsage`). If usage extraction failed (recorded as `usageError`) or was absent, a success cannot be evidenced, so the run throws — using the underlying usage-validation error's message as the code when available, else `cave_claude_terminal_missing`. The library fails closed because every past-the-gate success must carry a provider-reported usage receipt; an unevidenced success is treated as a terminal failure.
Solutions
- Read the thrown code (`usageError.message`, e.g. `cave_provider_usage_incomplete`) to see which usage field was missing, then fix the transport that drops it.
- Remove intermediaries (custom proxy, request rewriter) or re-add the `usage` block to responses.
- Pin/restore the exact-pinned Claude Agent SDK version whose result message carries aggregate usage.
- If this happens in tests, make SDK fakes emit realistic `usage` with input/output tokens.
Example fix
// before (test fake)
const fakeResult = { subtype: "success", result: "ok" };
// after
const fakeResult = { subtype: "success", is_error: false, result: "ok", usage: { input_tokens: 100, output_tokens: 20 } }; Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure no proxy strips usage: assert a probe response carries the usage block.
const probe = await fetch(baseUrl + "/v1/messages", { method: "POST", /* minimal request */ });
const body = await probe.json();
if (!body.usage) throw new Error("transport strips usage — accountable runs will fail"); Type guard
function hasUsage(r: unknown): r is { usage: { input_tokens: number; output_tokens: number } } {
return typeof r === "object" && r !== null && "usage" in r &&
typeof (r as any).usage?.input_tokens === "number";
} Try / catch
try {
const r = await runClaudeAgent(opts);
} catch (e) {
if (e instanceof CavemanRunError && e.code.startsWith("cave_provider_usage")) {
// the code names the exact missing usage field; fix the transport or SDK pin
}
throw e;
} Prevention
- Never put a response-rewriting proxy in front of the Anthropic API for evidenced runs.
- Keep the Claude Agent SDK at the exact-pinned version.
- Make SDK fakes/mocks emit full usage objects in tests.
- Run `caveman doctor` to surface transport/route issues before spending.
When it happens
Trigger: The SDK returned `subtype: "success"` but `result.usage` is missing/zero/incomplete so `usageFromSDKResult` threw (e.g. `cave_provider_usage_incomplete`); a proxy or gateway stripped usage fields; an SDK version that omits aggregate usage on the result message.
Common situations: Running behind custom gateways/proxies that drop the `usage` object; mocking or stubbing SDK responses in tests without usage; exotic auth regimes (OAuth subscription flows) where the SDK reports no per-run usage; SDK upgrades changing the result shape.
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_provider_model_identity_mismatch
- cave_subagent_spend_evidence_incomplete
- bedrock request path
- cave_fixture_terminal_evidence_missing
- cave_harness_incomplete_evidence
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/bab5282bd1fa5e44.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/claude-runtime.ts:351
);
}
const text = result.result;
if (definition.output?.schema !== undefined) {
let parsed: unknown;
try {
parsed = result.structured_output ?? JSON.parse(text);
} catch {
throw carry("cave_output_schema_invalid_json", "the SDK output was not valid JSON");
}
if (!Value.Check(definition.output.schema, parsed)) {
throw carry("cave_output_schema_mismatch", "the SDK output did not match the declared schema");
}
}
// Past the success gate, usage must be accountable. A success whose usage
// validateProviderUsage rejected is a real evidence failure — surface it
// carrying the receipt, never mask it.
if (usage === undefined) {
throw carry(
usageError?.message ?? "cave_claude_terminal_missing",
"the SDK reported no accountable usage on a successful result",
);
}
if (definition.output !== undefined && usage.outputTokens > definition.output.maxTokens) {
// The SDK already ran and spent by the time its aggregate usage is known,
// so this post-hoc budget breach carries the receipt of what was spent
// rather than throwing it away.
throw carry(
"cave_output_budget_exceeded",
`output ${usage.outputTokens} exceeded budget ${definition.output.maxTokens}`,
);
}
return {
runId: runID,
agentId: definition.id,
text,
contextIR: lowered.ir,View on GitHub (pinned to 3ee70a1026)