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
- Confirm `@mastra/core@1.55.0` is actually installed — `assertSupportedUpstream` enforces exactly that version
- Run a raw `agent.generate` and assert `response.totalUsage.inputTokens` is a non-negative integer
- 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
- Pin @mastra/core@1.55.0 — assertSupportedUpstream rejects any other installed version
- If you wrap the agent in a custom binding, return the documented FullOutput shape
- Run the usage smoke test in CI so usage-contract drift fails early
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
- cave_vercel_usage_missing
- cave_eve_usage_missing
- cave_mastra_terminal_failure
- cave_eve_terminal_${result.status}
- cave_harness_incomplete_evidence
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)