{"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":"exception","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/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/adapters.ts#L490-L526","documentation":"After a harness call finishes, the response must report which model produced the output, and that reported model must be a non-empty string. `normalizedResponseModel` reads the model identity from the upstream response (optionally stripping a `provider/` prefix) so the adapter can verify the response model matches the plan; a missing or empty value means the evidence chain for model identity is incomplete, so the adapter fails closed. This is an upstream-response contract violation, not a caller configuration error.","triggerScenarios":"The upstream harness (Vercel AI SDK / Eve / Mastra) returns a response object whose model field is `undefined`, `null`, `\"\"`, or a non-string — e.g. a mock transport in tests, a response-shaping middleware that strips fields, or an upstream API change that renames the field.","commonSituations":"Unit tests stubbing harness responses with `{ text: \"...\" }` and forgetting `model`; an HTTP proxy or serializer dropping the model field; upgrading the pinned harness SDK after an API rename without regenerating the adapter evidence; using a custom fetch transport that fabricates responses.","solutions":["If you stub or record harness responses, include a non-empty `model` string such as `\"anthropic/claude-sonnet-4-5\"`.","Remove any middleware or proxy that strips fields from the upstream response body.","If the error appears after upgrading the pinned harness package, reinstall the exact pinned version (e.g. Vercel AI SDK 7.0.43)."],"exampleFix":"// before (test stub)\nconst response = { text: \"done\", usage: { /* ... */ } };\n\n// after (test stub)\nconst response = { model: \"anthropic/claude-sonnet-4-5\", text: \"done\", usage: { /* ... */ } };","handlingStrategy":"validation","validationCode":"function hasResponseModel(response: unknown): boolean {\n  return typeof (response as Record<string, unknown> | null | undefined)?.model === \"string\" &&\n    ((response as Record<string, unknown>).model as string).length > 0;\n}","typeGuard":"function hasModelField<T extends object>(v: T): v is T & { model: string } {\n  return typeof (v as Record<string, unknown>).model === \"string\" &&\n    ((v as Record<string, unknown>).model as string).length > 0;\n}","tryCatchPattern":"try {\n  await adapter.run(request);\n} catch (err) {\n  if (err instanceof Error && err.message === \"cave_harness_model_identity_missing\") {\n    // upstream response lacked model identity: fix the transport/stub, do not retry blindly\n  }\n}","preventionTips":["Include the model field in every recorded/stubbed harness response.","Keep the pinned harness SDK version so response shapes don't drift.","Assert response contract fields in test fixtures once, in a shared helper."],"tags":["adapter","model-id","response-contract","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}