{"record":{"id":"8ed8a7d08767ae5a","repo":"JuliusBrussee/caveman","slug":"cave-harness-model-invalid-8ed8a7","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/execution-kernel.ts","lineNumber":94,"sourceCode":"    throw new Error(\"cave_harness_build_mismatch\");\n  }\n  if (input.agentId !== undefined && build.agent_id !== input.agentId) {\n    throw new Error(\"cave_harness_agent_mismatch\");\n  }\n  if (stableStringify(input.plan) !== stableStringify(build.selected_plan)) {\n    throw new Error(\"cave_harness_plan_mismatch\");\n  }\n  const planSHA256 = sha256(stableStringify(input.plan));\n  if (planSHA256 !== build.plan_sha256) {\n    throw new Error(\"cave_harness_plan_digest_mismatch\");\n  }\n  const contextIRSHA256 = sha256(stableStringify(contextIRToWire(input.contextIR)));\n  if (contextIRSHA256 !== build.context_ir_sha256) {\n    throw new Error(\"cave_harness_context_ir_mismatch\");\n  }\n  const [provider, ...modelParts] = input.plan.model.split(\"/\");\n  const model = modelParts.join(\"/\");\n  if (!provider || !model) throw new Error(\"cave_harness_model_invalid\");\n  return Object.freeze({\n    build,\n    plan: build.selected_plan,\n    planSHA256,\n    contextIRSHA256,\n    provider,\n    model,\n  });\n}\n\nexport interface ProviderUsageEvidence {\n  provider: string;\n  model: string;\n  inputTokens: number;\n  outputTokens: number;\n  cacheReadTokens: number;\n  cacheWriteTokens: number;\n  reasoningTokens: number;","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/execution-kernel.ts#L76-L112","documentation":"Thrown while preparing a locked harness (packages/agent/src/execution-kernel.ts:94): the selected plan's model string does not split into a non-empty provider and a non-empty model. The framework expects 'provider/model' format (e.g. 'anthropic/claude-sonnet-4-5'); splitting on the first '/' must leave both parts non-empty.","triggerScenarios":"plan.model is a bare model name with no slash ('claude-sonnet-4-5'), starts or ends with '/' ('/model', 'anthropic/'), or is the empty string.","commonSituations":"Hand-writing a model string without the provider prefix; configuring the model from an env var that is empty or unpopulated; copying a model id from provider docs that omits the vendor prefix; string-manipulation bugs trimming the provider segment.","solutions":["Set the model to a 'provider/model' string, e.g. 'anthropic/claude-sonnet-4-5'.","If the model comes from configuration, validate it matches /^[^/]+\\/.+$/ before building/running.","Use the framework's model helpers/typed model constants instead of raw strings where available."],"exampleFix":"// before\nagent({ model: \"claude-sonnet-4-5\", /* ... */ });\n\n// after\nagent({ model: \"anthropic/claude-sonnet-4-5\", /* ... */ });","handlingStrategy":"type-guard","validationCode":"const MODEL_RE = /^[^/]+\\/.+$/;\nfunction parseProviderModel(model: string): { provider: string; model: string } | null {\n  const idx = model.indexOf(\"/\");\n  if (idx <= 0 || idx === model.length - 1) return null;\n  return { provider: model.slice(0, idx), model: model.slice(idx + 1) };\n}\nconst parsed = parseProviderModel(plan.model);\nif (!parsed) throw new Error(\"model must be 'provider/model'\");","typeGuard":"function isQualifiedModel(value: unknown): value is string {\n  return typeof value === \"string\" && /^[^/]+\\/.+$/.test(value) && !value.endsWith(\"/\");\n}","tryCatchPattern":null,"preventionTips":["Always configure models as 'provider/model' (e.g. 'anthropic/claude-sonnet-4-5').","Validate model strings from env vars/config with a regex before passing them to agent().","Centralize model constants in one module instead of scattering raw strings."],"tags":["model","format","validation","harness"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}