{"record":{"id":"88cce697c87ea53c","repo":"JuliusBrussee/caveman","slug":"cave-claude-model-required","errorCode":null,"errorMessage":"cave_claude_model_required","messagePattern":"cave_claude_model_required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/claude-runtime.ts","lineNumber":468,"sourceCode":"  if (/[:\\r\\n]/.test(name) || /[\\r\\n]/.test(value)) {\n    throw new Error(\"cave_claude_header_invalid\");\n  }\n  const target = name.toLowerCase();\n  const kept = (raw ?? \"\").split(/\\r\\n|\\n|\\r/).filter((line) => {\n    if (!line.trim()) return false;\n    const colon = line.indexOf(\":\");\n    return (colon < 0 ? line : line.slice(0, colon)).trim().toLowerCase() !== target;\n  });\n  kept.push(`${name}: ${value}`);\n  return kept.join(\"\\n\");\n}\n\nfunction resolveClaudeModel(definition: AgentDefinition, rootDir: string): string {\n  const configured = typeof definition.model === \"string\"\n    ? definition.model\n    : process.env.CAVE_MODEL ?? localModel(rootDir) ??\n      (process.env.ANTHROPIC_API_KEY ? \"anthropic/claude-haiku-4-5\" : undefined);\n  if (configured === undefined) throw new Error(\"cave_claude_model_required\");\n  const separator = configured.indexOf(\"/\");\n  if (separator < 1 || configured.slice(0, separator) !== \"anthropic\") {\n    throw new Error(\"cave_claude_provider_unsupported\");\n  }\n  return normalizeClaudeModel(configured.slice(separator + 1));\n}\n\nfunction localModel(rootDir: string): string | undefined {\n  try {\n    const parsed = JSON.parse(readFileSync(resolve(rootDir, \".caveman/provider.json\"), \"utf8\")) as {\n      model?: unknown;\n    };\n    return typeof parsed.model === \"string\" ? parsed.model : undefined;\n  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n    throw new Error(\"caveman agent: invalid .caveman/provider.json\");\n  }\n}","sourceCodeStart":450,"sourceCodeEnd":486,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/claude-runtime.ts#L450-L486","documentation":"resolveClaudeModel() found no model to run: definition.model is not a string, CAVE_MODEL is unset, .caveman/provider.json has no usable model, and ANTHROPIC_API_KEY is absent so even the haiku default cannot be chosen. The Claude harness needs a concrete model id before it can construct the SDK query, so it throws instead of guessing.","triggerScenarios":"Calling the Claude run path with an AgentDefinition lacking `model`, no CAVE_MODEL in env, no .caveman/provider.json (or one without a string `model` field), and no ANTHROPIC_API_KEY set — the final ternary yields undefined and the guard fires.","commonSituations":"Fresh clone without env setup; CI job that intentionally omits ANTHROPIC_API_KEY to avoid spend but still routes a build to the Claude harness; provider.json present but with model set to a non-string (number/object) or misspelled key.","solutions":["Set model explicitly on the AgentDefinition: { model: \"anthropic/claude-haiku-4-5\", ... }.","Or export CAVE_MODEL=anthropic/claude-haiku-4-5 in the environment the agent runs in.","Or create .caveman/provider.json with { \"model\": \"anthropic/claude-sonnet-4-5\" } at the project root.","Or set ANTHROPIC_API_KEY so the built-in haiku default applies."],"exampleFix":"// before\nconst definition = { id: \"triage\", /* no model */ };\n\n// after\nconst definition = { id: \"triage\", model: \"anthropic/claude-haiku-4-5\" };","handlingStrategy":"validation","validationCode":"function hasClaudeModel(definition: AgentDefinition, rootDir: string): boolean {\n  return typeof definition.model === \"string\"\n    || Boolean(process.env.CAVE_MODEL)\n    || existsSync(join(rootDir, \".caveman\", \"provider.json\"))\n    || Boolean(process.env.ANTHROPIC_API_KEY);\n}","typeGuard":"function hasModel(definition: AgentDefinition): definition is AgentDefinition & { model: string } {\n  return typeof definition.model === \"string\" && definition.model.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Always set an explicit model on AgentDefinition for the Claude harness — don't rely on the env/file fallback chain.","In CI, export CAVE_MODEL or ANTHROPIC_API_KEY explicitly so resolution never depends on ambient state.","Keep .caveman/provider.json checked in for projects that pin a default model locally."],"tags":["configuration","model-selection","claude-sdk","env"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}