{"record":{"id":"aefd5524e00afdc6","repo":"JuliusBrussee/caveman","slug":"cave-provider-identity-missing","errorCode":"cave_provider_identity_missing","errorMessage":"cave_provider_identity_missing","messagePattern":"cave_provider_identity_missing","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/execution-kernel.ts","lineNumber":131,"sourceCode":"  totalTokens: number;\n}\n\nexport interface ValidatedProviderUsage extends ProviderUsageEvidence {\n  priced: boolean;\n  catalogCostUsd: number;\n}\n\nexport function validateProviderUsage(\n  evidence: ProviderUsageEvidence,\n  options: {\n    expected?: { provider: string; model: string };\n    reportedCostUsd?: number;\n    requirePriced?: boolean;\n  } = {},\n): ValidatedProviderUsage {\n  if (typeof evidence.provider !== \"string\" || evidence.provider.length === 0 ||\n      typeof evidence.model !== \"string\" || evidence.model.length === 0) {\n    throw new Error(\"cave_provider_identity_missing\");\n  }\n  if (options.expected !== undefined &&\n      (evidence.provider !== options.expected.provider || evidence.model !== options.expected.model)) {\n    throw new Error(\"cave_provider_model_identity_mismatch\");\n  }\n  const values = [\n    evidence.inputTokens,\n    evidence.outputTokens,\n    evidence.cacheReadTokens,\n    evidence.cacheWriteTokens,\n    evidence.reasoningTokens,\n    evidence.totalTokens,\n  ];\n  const disjointTotal = evidence.inputTokens + evidence.outputTokens +\n    evidence.cacheReadTokens + evidence.cacheWriteTokens;\n  if (values.some((value) => !Number.isSafeInteger(value) || value < 0) ||\n      evidence.totalTokens <= 0 || evidence.totalTokens !== disjointTotal ||\n      evidence.reasoningTokens > evidence.outputTokens) {","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/execution-kernel.ts#L113-L149","documentation":"Thrown by validateProviderUsage (packages/agent/src/execution-kernel.ts:131): the provider usage evidence lacks a usable identity — provider or model is not a string, or is an empty string. Every reserved turn must report complete usage with an exact provider/model identity; evidence without identity cannot be billed or trusted and fails closed.","triggerScenarios":"Calling validateProviderUsage with evidence whose provider/model fields are missing, undefined, non-string, or empty — typically usage parsed from an adapter or SDK response that did not populate the model field.","commonSituations":"A provider/adapter returning usage without a model name; destructuring or mapping usage objects and dropping fields; streaming paths where usage is aggregated from partial chunks and identity is lost; mocking provider responses in tests without identity fields.","solutions":["Ensure the usage evidence passed in carries the provider's reported provider and model strings verbatim.","If an upstream SDK omits the model in usage, take it from the response's model field and attach it to the evidence before validation.","In tests, populate provider/model on fixture usage objects instead of only token counts."],"exampleFix":"// before\nvalidateProviderUsage({ /* provider/model missing */ inputTokens: 10, outputTokens: 5, ... });\n\n// after\nvalidateProviderUsage({ provider: \"anthropic\", model: \"claude-sonnet-4-5\", inputTokens: 10, outputTokens: 5, cacheReadTokens: 0, cacheWriteTokens: 0, reasoningTokens: 0, totalTokens: 15 });","handlingStrategy":"type-guard","validationCode":"function hasUsageIdentity(e: { provider?: unknown; model?: unknown }): boolean {\n  return typeof e.provider === \"string\" && e.provider.length > 0 &&\n         typeof e.model === \"string\" && e.model.length > 0;\n}\nif (!hasUsageIdentity(evidence)) throw new Error(\"usage evidence lacks provider/model identity\");","typeGuard":"interface ProviderUsageEvidence { provider: string; model: string; inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheWriteTokens: number; reasoningTokens: number; totalTokens: number; }\nfunction isProviderUsageEvidence(v: unknown): v is ProviderUsageEvidence {\n  const e = v as Record<string, unknown>;\n  return typeof e?.provider === \"string\" && (e.provider as string).length > 0 &&\n    typeof e?.model === \"string\" && (e.model as string).length > 0 &&\n    [\"inputTokens\",\"outputTokens\",\"cacheReadTokens\",\"cacheWriteTokens\",\"reasoningTokens\",\"totalTokens\"].every((k) => typeof e[k] === \"number\");\n}","tryCatchPattern":null,"preventionTips":["Always attach the provider-reported model string to aggregated usage before validation.","When mapping adapter responses, write a mapper that guarantees provider/model presence.","In test fixtures, populate identity fields, not just token counts."],"tags":["usage","provider","validation","billing"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}