{"record":{"id":"b98fff6bf0388ec2","repo":"JuliusBrussee/caveman","slug":"cave-mastra-usage-missing","errorCode":"cave_mastra_usage_missing","errorMessage":"cave_mastra_usage_missing","messagePattern":"cave_mastra_usage_missing","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/adapters.ts","lineNumber":407,"sourceCode":"    throw new Error(\"cave_vercel_usage_missing\");\n  }\n  const normalized = normalizeUsage({\n    inputTokens: noCache,\n    outputTokens: usage.outputTokens,\n    cacheReadTokens: cacheRead,\n    cacheWriteTokens: cacheWrite,\n    reasoningTokens: output.reasoningTokens,\n    totalTokens: usage.totalTokens,\n  }, reasoningRequired, \"cave_vercel_usage_missing\");\n  return normalized;\n}\n\nfunction usageFromMastra(value: unknown, reasoningRequired: boolean): NormalizedUsage {\n  const usage = record(value, \"cave_mastra_usage_missing\");\n  const totalInput = strictInteger(usage.inputTokens, \"cave_mastra_usage_missing\");\n  const cacheRead = optionalInteger(usage.cachedInputTokens, \"cave_mastra_usage_missing\");\n  const cacheWrite = optionalInteger(usage.cacheCreationInputTokens, \"cave_mastra_usage_missing\");\n  if (cacheRead + cacheWrite > totalInput) throw new Error(\"cave_mastra_usage_missing\");\n  return normalizeUsage({\n    inputTokens: totalInput - cacheRead - cacheWrite,\n    outputTokens: usage.outputTokens,\n    cacheReadTokens: cacheRead,\n    cacheWriteTokens: cacheWrite,\n    reasoningTokens: usage.reasoningTokens,\n    totalTokens: usage.totalTokens,\n  }, reasoningRequired, \"cave_mastra_usage_missing\");\n}\n\nfunction usageFromEveEvents(events: unknown[], reasoningRequired: boolean): NormalizedUsage {\n  let totalInputTokens = 0;\n  let outputTokens = 0;\n  let cacheReadTokens = 0;\n  let cacheWriteTokens = 0;\n  let steps = 0;\n  for (const event of events) {\n    if (!isRecord(event) || event.type !== \"step.completed\" || !isRecord(event.data)) continue;","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/agent/src/adapters.ts#L389-L425","documentation":"`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.","triggerScenarios":"`response.totalUsage` and `response.usage` both undefined/empty on the FullOutput; `totalUsage.inputTokens` missing or non-integer; `cachedInputTokens` reported without a valid `inputTokens`.","commonSituations":"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.","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`"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// pre-flight: one raw generate must return a complete totalUsage record\nconst out = await agent.generate('ping', { maxProcessorRetries: 0 });\nconst u = out.totalUsage ?? out.usage;\nconst ok = u && Number.isSafeInteger(u.inputTokens) && u.inputTokens >= 0 &&\n  (u.cachedInputTokens === undefined || Number.isSafeInteger(u.cachedInputTokens));\nif (!ok) throw new Error('binding does not return FullOutput.totalUsage');","typeGuard":"function hasMastraUsage(u: unknown): boolean {\n  if (typeof u !== 'object' || u === null) return false;\n  const s = u as Record<string, any>;\n  return Number.isSafeInteger(s.inputTokens) && Number(s.inputTokens) >= 0;\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["typescript","mastra","token-usage","llm","accounting"],"backgroundTag":"llm-token-usage-missing","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}