{"record":{"id":"514de8bb26fa60c0","repo":"JuliusBrussee/caveman","slug":"cave-harness-model-invalid","errorCode":"cave_harness_model_invalid","errorMessage":"cave_harness_model_invalid","messagePattern":"cave_harness_model_invalid","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/adapters.ts","lineNumber":501,"sourceCode":"      runtime.modelId.startsWith(\"dynamic:\")) {\n    throw new Error(\"cave_eve_runtime_identity_missing\");\n  }\n  const separator = runtime.modelId.indexOf(\"/\");\n  if (separator < 1 || separator === runtime.modelId.length - 1 ||\n      typeof runtime.eveVersion !== \"string\" || runtime.eveVersion.length === 0) {\n    throw new Error(\"cave_eve_runtime_identity_missing\");\n  }\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;","sourceCodeStart":483,"sourceCodeEnd":519,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/adapters.ts#L483-L519","documentation":"The harness plan's `model` field must be a `provider/model` string with a non-empty provider segment before the first `/` and a non-empty model segment after it. `expectedProviderModel` splits on the first `/` to derive the provider identity used for usage and cost validation; a bare model name or a leading/trailing slash has no unambiguous provider, so the adapter fails closed. This indicates the plan handed to the adapter was constructed or edited incorrectly.","triggerScenarios":"Passing a `CavePlan` whose `model` is `\"claude-sonnet-4-5\"` (no provider), `\"/claude-sonnet-4-5\"` (empty provider), `\"anthropic/\"` (empty model), or an empty string. Any call that lowers a plan into the Vercel/Eve/Mastra bridge hits this in `expectedProviderModel`.","commonSituations":"Hand-writing a plan instead of compiling one through the framework; copying a model name from a provider console (bare names like `gpt-4o`) into plan config; a config loader trimming or mangling the `provider/model` value; changing model naming conventions between environments.","solutions":["Set `plan.model` to the full `provider/model` form, e.g. `\"openai/gpt-4o\"` or `\"anthropic/claude-sonnet-4-5\"`.","If the plan comes from your own config file, add a startup assertion that `model` matches `/^[^/]+\\/.+[^/]$/` before invoking the adapter.","Regenerate the plan via the framework compiler instead of hand-editing it."],"exampleFix":"// before\nconst plan = { model: \"claude-sonnet-4-5\", /* ... */ };\n\n// after\nconst plan = { model: \"anthropic/claude-sonnet-4-5\", /* ... */ };","handlingStrategy":"type-guard","validationCode":"function isValidPlanModel(model: string): boolean {\n  const i = model.indexOf(\"/\");\n  return i >= 1 && i < model.length - 1;\n}\nif (!isValidPlanModel(plan.model)) throw new Error(\"plan.model must be provider/model\");","typeGuard":"function isProviderModel(value: string): value is `${string}/${string}` {\n  const i = value.indexOf(\"/\");\n  return i >= 1 && i < value.length - 1;\n}","tryCatchPattern":"try {\n  await adapter.run(request);\n} catch (err) {\n  if (err instanceof Error && err.message === \"cave_harness_model_invalid\") {\n    plan.model = `anthropic/${plan.model}`; // repair only if provider is genuinely known\n  }\n}","preventionTips":["Type plan.model as `${string}/${string}` at the config boundary.","Always compile plans through the framework rather than hand-writing them.","Assert the provider/model shape in config-loading code before run time."],"tags":["adapter","plan","model-id","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}