JuliusBrussee/caveman · error · Error

cave_provider_usage_incomplete

cave_provider_usage_incomplete

Error message

cave_provider_usage_incomplete

What it means

Thrown by validateProviderUsage (packages/agent/src/execution-kernel.ts:150): the token usage evidence is internally inconsistent or incomplete — a count is not a safe non-negative integer, totalTokens is <= 0, totalTokens does not equal input+output+cacheRead+cacheWrite, or reasoningTokens exceeds outputTokens. Complete usage is mandatory for reserved turns; arithmetic that does not add up is rejected.

Source

Thrown at packages/agent/src/execution-kernel.ts:150

  }
  if (options.expected !== undefined &&
      (evidence.provider !== options.expected.provider || evidence.model !== options.expected.model)) {
    throw new Error("cave_provider_model_identity_mismatch");
  }
  const values = [
    evidence.inputTokens,
    evidence.outputTokens,
    evidence.cacheReadTokens,
    evidence.cacheWriteTokens,
    evidence.reasoningTokens,
    evidence.totalTokens,
  ];
  const disjointTotal = evidence.inputTokens + evidence.outputTokens +
    evidence.cacheReadTokens + evidence.cacheWriteTokens;
  if (values.some((value) => !Number.isSafeInteger(value) || value < 0) ||
      evidence.totalTokens <= 0 || evidence.totalTokens !== disjointTotal ||
      evidence.reasoningTokens > evidence.outputTokens) {
    throw new Error("cave_provider_usage_incomplete");
  }
  const priced = catalogCost({
    provider: evidence.provider,
    model: evidence.model,
    inputTokens: evidence.inputTokens,
    outputTokens: evidence.outputTokens,
    cacheReadTokens: evidence.cacheReadTokens,
    cacheWriteTokens: evidence.cacheWriteTokens,
    reasoningTokens: evidence.reasoningTokens,
  });
  if (!priced.priced && options.requirePriced === true) {
    throw new Error("cave_provider_usage_unpriced");
  }
  if (options.reportedCostUsd !== undefined &&
      (!priced.priced || !Number.isFinite(options.reportedCostUsd) ||
        options.reportedCostUsd < 0 ||
        Math.abs(options.reportedCostUsd - priced.usd) > 1e-12)) {
    throw new Error("cave_provider_cost_mismatch");

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Default missing optional counters (cacheRead, cacheWrite, reasoning) to 0 when building evidence rather than passing undefined.
  2. Recompute totalTokens as input+output+cacheRead+cacheWrite before validating, and ensure totalTokens > 0 for a real call.
  3. For reasoning-capable models, make sure reasoningTokens is a subset of outputTokens as reported; if the provider reports them disjoint, map them into the fields the validator expects.

Example fix

// before
validateProviderUsage({ provider, model, inputTokens: 10, outputTokens: 5 }); // missing fields -> NaN

// after
validateProviderUsage({ provider, model, inputTokens: 10, outputTokens: 5, cacheReadTokens: 0, cacheWriteTokens: 0, reasoningTokens: 0, totalTokens: 15 });
Defensive patterns

Strategy: validation

Validate before calling

function normalizeUsage(u: Partial<ProviderUsageEvidence>): ProviderUsageEvidence {
  const n = (v: number | undefined) => (Number.isSafeInteger(v) && v! >= 0 ? v! : 0);
  const input = n(u.inputTokens), output = n(u.outputTokens);
  const cacheRead = n(u.cacheReadTokens), cacheWrite = n(u.cacheWriteTokens);
  const reasoning = Math.min(n(u.reasoningTokens), output);
  return { provider: u.provider!, model: u.model!, inputTokens: input, outputTokens: output,
    cacheReadTokens: cacheRead, cacheWriteTokens: cacheWrite, reasoningTokens: reasoning,
    totalTokens: input + output + cacheRead + cacheWrite };
}
validateProviderUsage(normalizeUsage(rawUsage));

Type guard

function isConsistentUsage(e: ProviderUsageEvidence): boolean {
  const vals = [e.inputTokens, e.outputTokens, e.cacheReadTokens, e.cacheWriteTokens, e.reasoningTokens, e.totalTokens];
  return vals.every((v) => Number.isSafeInteger(v) && v >= 0) &&
    e.totalTokens > 0 &&
    e.totalTokens === e.inputTokens + e.outputTokens + e.cacheReadTokens + e.cacheWriteTokens &&
    e.reasoningTokens <= e.outputTokens;
}

Prevention

When it happens

Trigger: Passing usage where any of the six token fields is fractional, negative, undefined (NaN), or exceeds Number.MAX_SAFE_INTEGER; totalTokens set to 0 or omitted; totals that don't equal the sum of the disjoint components; reasoning tokens larger than output tokens.

Common situations: Adapters that omit cache/reasoning fields (undefined -> NaN in the sum) instead of defaulting them to 0; aggregating usage across streaming chunks and losing/adding tokens; providers reporting reasoning tokens counted outside output; hand-built usage objects in tests with totalTokens: 0.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/c12fa818ca203b46. Report an issue: GitHub.