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

  1. 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.
  2. Remove intermediaries (custom proxy, request rewriter) or re-add the `usage` block to responses.
  3. Pin/restore the exact-pinned Claude Agent SDK version whose result message carries aggregate usage.
  4. 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

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


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)