{"record":{"id":"d803ddd4e1d634a6","repo":"JuliusBrussee/caveman","slug":"cave-harness-model-identity-missing","errorCode":"cave_harness_model_identity_missing","errorMessage":"cave_harness_model_identity_missing","messagePattern":"cave_harness_model_identity_missing","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/adapters.ts","lineNumber":508,"sourceCode":"  }\n  return {\n    provider: runtime.modelId.slice(0, separator),\n    model: runtime.modelId.slice(separator + 1),\n    upstreamVersion: runtime.eveVersion,\n  };\n}\n\nfunction expectedProviderModel(plan: CavePlan): { provider: string; model: string } {\n  const separator = plan.model.indexOf(\"/\");\n  if (separator < 1 || separator === plan.model.length - 1) {\n    throw new Error(\"cave_harness_model_invalid\");\n  }\n  return { provider: plan.model.slice(0, separator), model: plan.model.slice(separator + 1) };\n}\n\nfunction normalizedResponseModel(value: unknown, provider: string): string {\n  if (typeof value !== \"string\" || value.length === 0) {\n    throw new Error(\"cave_harness_model_identity_missing\");\n  }\n  return value.startsWith(`${provider}/`) ? value.slice(provider.length + 1) : value;\n}\n\nfunction terminalFinishReason(value: unknown): boolean {\n  return typeof value === \"string\" && [\"stop\", \"length\", \"content-filter\", \"other\"].includes(value);\n}\n\nfunction record(value: unknown, error: string): Record<string, unknown> {\n  if (!isRecord(value)) throw new Error(error);\n  return value;\n}\n\nfunction strictInteger(value: unknown, error: string): number {\n  if (!Number.isSafeInteger(value) || Number(value) < 0) throw new Error(error);\n  return Number(value);\n}\n","sourceCodeStart":490,"sourceCodeEnd":526,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/agent/src/adapters.ts#L490-L526","documentation":"After generate() returns, the Vercel and Mastra adapters read response.response.modelId and pass it to normalizedResponseModel, which throws unless it is a non-empty string. The framework requires the upstream response itself to name the model that answered; it will not infer identity from the plan, so an absent modelId is treated as unverifiable execution.","triggerScenarios":"adapter.run() on a vercel-ai-sdk or mastra adapter when the binding's generate() result has response undefined, response.modelId undefined, or an empty string — e.g. a custom LanguageModel wrapper, a gateway/proxy that strips response metadata, an off-pin ai/@mastra/core version with a different response shape, or a mocked binding.","commonSituations":"Wrapping ToolLoopAgent or Mastra Agent with a custom transport that forgets to forward response; test doubles returning only { text, usage }; upgrading the upstream SDK so modelId moves or disappears.","solutions":["Smoke-test the binding before wiring it into the adapter: one generate() call must return a non-empty response.modelId","If you wrap the upstream agent/transport, forward the response object (including modelId) verbatim","Keep the exact pinned upstream (ai 7.0.43, @mastra/core 1.55.0) so the documented response shape holds"],"exampleFix":"// before (mocked binding)\nconst agent = { generate: async () => ({ text: \"hi\", usage: {...} }) }; // no response.modelId\n\n// after\nconst agent = {\n  generate: async () => ({\n    text: \"hi\",\n    usage: {...},\n    response: { modelId: \"anthropic/claude-sonnet-4-5\" },\n  }),\n};","handlingStrategy":"validation","validationCode":"// Smoke-test the binding once before wiring it into the adapter:\nconst probe = await agent.generate({ prompt: \"ping\" });\nif (typeof probe.response?.modelId !== \"string\" || probe.response.modelId.length === 0) {\n  throw new Error(\"binding must surface a non-empty response.modelId\");\n}","typeGuard":"function reportsResponseModel<T extends { response?: { modelId?: unknown } }>(\n  value: T,\n): boolean {\n  return typeof value.response?.modelId === \"string\" && value.response.modelId.length > 0;\n}","tryCatchPattern":"try {\n  const result = await adapter.run(request);\n} catch (error) {\n  if (error instanceof Error && error.message === \"cave_harness_model_identity_missing\") {\n    // The upstream response did not name the model it used; fix the\n    // agent/transport so response.modelId survives, then retry.\n  }\n  throw error;\n}","preventionTips":["Forward the upstream response object verbatim in any wrapper around ToolLoopAgent or Mastra Agent","Keep the exact pinned ai/@mastra/core versions so the response shape is the documented one","Include response.modelId in every test double for these bindings"],"tags":["vercel-ai-sdk","mastra","model-identity","adapter","fail-closed"],"backgroundTag":"missing-model-identity-in-response","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"}