JuliusBrussee/caveman · error · Error

cave_mastra_usage_missing

cave_mastra_usage_missing

Error message

cave_mastra_usage_missing

What it means

`usageFromMastra` validates the `FullOutput.totalUsage` record (falling back to `usage`): `inputTokens` must be a non-negative safe integer, `cachedInputTokens`/`cacheCreationInputTokens` must be integers or absent, their sum must not exceed `inputTokens`, and `normalizeUsage` then requires `outputTokens`/`totalTokens` (plus `reasoningTokens` when the plan requires reasoning accounting) to reconcile. Any gap throws `cave_mastra_usage_missing` — missing token accounting is treated as missing evidence, not as zero.

Solutions

  1. Confirm `@mastra/core@1.55.0` is actually installed — `assertSupportedUpstream` enforces exactly that version
  2. Run a raw `agent.generate` and assert `response.totalUsage.inputTokens` is a non-negative integer
  3. If you pass a custom binding, return the documented FullOutput shape including `totalUsage`
Defensive patterns

Strategy: validation

Validate before calling

// pre-flight: one raw generate must return a complete totalUsage record
const out = await agent.generate('ping', { maxProcessorRetries: 0 });
const u = out.totalUsage ?? out.usage;
const ok = u && Number.isSafeInteger(u.inputTokens) && u.inputTokens >= 0 &&
  (u.cachedInputTokens === undefined || Number.isSafeInteger(u.cachedInputTokens));
if (!ok) throw new Error('binding does not return FullOutput.totalUsage');

Type guard

function hasMastraUsage(u: unknown): boolean {
  if (typeof u !== 'object' || u === null) return false;
  const s = u as Record<string, any>;
  return Number.isSafeInteger(s.inputTokens) && Number(s.inputTokens) >= 0;
}

Prevention

When it happens

Trigger: `response.totalUsage` and `response.usage` both undefined/empty on the FullOutput; `totalUsage.inputTokens` missing or non-integer; `cachedInputTokens` reported without a valid `inputTokens`.

Common situations: A custom `MastraAgentBinding`/mock that returns a different usage shape than documented; provider integrations that don't populate `totalUsage`; streaming paths where usage arrives detached from the final output.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18). Data as JSON: /api/errors/b98fff6bf0388ec2. Report an issue: GitHub.

Appendix: source

Thrown at packages/agent/src/adapters.ts:407

    throw new Error("cave_vercel_usage_missing");
  }
  const normalized = normalizeUsage({
    inputTokens: noCache,
    outputTokens: usage.outputTokens,
    cacheReadTokens: cacheRead,
    cacheWriteTokens: cacheWrite,
    reasoningTokens: output.reasoningTokens,
    totalTokens: usage.totalTokens,
  }, reasoningRequired, "cave_vercel_usage_missing");
  return normalized;
}

function usageFromMastra(value: unknown, reasoningRequired: boolean): NormalizedUsage {
  const usage = record(value, "cave_mastra_usage_missing");
  const totalInput = strictInteger(usage.inputTokens, "cave_mastra_usage_missing");
  const cacheRead = optionalInteger(usage.cachedInputTokens, "cave_mastra_usage_missing");
  const cacheWrite = optionalInteger(usage.cacheCreationInputTokens, "cave_mastra_usage_missing");
  if (cacheRead + cacheWrite > totalInput) throw new Error("cave_mastra_usage_missing");
  return normalizeUsage({
    inputTokens: totalInput - cacheRead - cacheWrite,
    outputTokens: usage.outputTokens,
    cacheReadTokens: cacheRead,
    cacheWriteTokens: cacheWrite,
    reasoningTokens: usage.reasoningTokens,
    totalTokens: usage.totalTokens,
  }, reasoningRequired, "cave_mastra_usage_missing");
}

function usageFromEveEvents(events: unknown[], reasoningRequired: boolean): NormalizedUsage {
  let totalInputTokens = 0;
  let outputTokens = 0;
  let cacheReadTokens = 0;
  let cacheWriteTokens = 0;
  let steps = 0;
  for (const event of events) {
    if (!isRecord(event) || event.type !== "step.completed" || !isRecord(event.data)) continue;

View on GitHub (pinned to 766dce6b13)