{"record":{"id":"dcae21f662ee14dd","repo":"JuliusBrussee/caveman","slug":"cave-claude-provider-unsupported","errorCode":"cave_claude_provider_unsupported","errorMessage":"cave_claude_provider_unsupported","messagePattern":"cave_claude_provider_unsupported","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/claude-runtime.ts","lineNumber":471,"sourceCode":"  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}\n\nfunction normalizeClaudeModel(model: string): string {\n  return model.startsWith(\"anthropic/\") ? model.slice(\"anthropic/\".length) : model;","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/claude-runtime.ts#L453-L489","documentation":"resolveClaudeModel resolves the Claude model from the agent definition, CAVE_MODEL env var, .caveman/provider.json, or a default when ANTHROPIC_API_KEY exists. The Claude runtime only accepts models whose provider prefix is exactly \"anthropic/\" (slash after at least one character). Any other provider prefix triggers this fail-closed guard because this executor only supports Anthropic models.","triggerScenarios":"Passing a model string like \"openai/gpt-4o\", \"bedrock/...\", \"openrouter/...\" via definition.model, CAVE_MODEL, or .caveman/provider.json; or a string with no \"/\" at all (indexOf returns -1) or one starting with \"/\" (separator < 1).","commonSituations":"Reusing a model id written for a multi-provider gateway (e.g. OpenRouter-style \"anthropic/claude-...\" is fine but \"google/gemini...\" is not); copying a provider.json from a project configured for another provider; forgetting the \"anthropic/\" prefix entirely.","solutions":["Prefix the model with \"anthropic/\", e.g. CAVE_MODEL=anthropic/claude-haiku-4-5 or model: \"anthropic/claude-sonnet-4-5\" in the definition.","If you need a non-Anthropic provider, use a different runtime/adapter (Pi runtime or adapters.ts bridges) instead of the Claude executor.","Check .caveman/provider.json and CAVE_MODEL for a stale provider prefix left by another tool."],"exampleFix":"// before\nprocess.env.CAVE_MODEL = \"openai/gpt-4o\";\n// after\nprocess.env.CAVE_MODEL = \"anthropic/claude-haiku-4-5\";","handlingStrategy":"validation","validationCode":"function isAnthropicModel(model) {\n  const i = model.indexOf(\"/\");\n  return i >= 1 && model.slice(0, i) === \"anthropic\";\n}\nconst model = definition.model ?? process.env.CAVE_MODEL;\nif (model !== undefined && !isAnthropicModel(model)) {\n  throw new Error(`refusing to run: ${model} is not an anthropic/ model`);\n}","typeGuard":"function isAnthropicModel(v) { return typeof v === \"string\" && v.indexOf(\"/\") >= 1 && v.slice(0, v.indexOf(\"/\")) === \"anthropic\"; }","tryCatchPattern":"try {\n  await runClaudeAgent(options);\n} catch (e) {\n  if (e.message === \"cave_claude_provider_unsupported\") {\n    console.error(`Model must be prefixed \"anthropic/\", got: ${options.model ?? process.env.CAVE_MODEL}`);\n  } else throw e;\n}","preventionTips":["Always use provider-qualified \"anthropic/<model>\" ids in CAVE_MODEL and provider.json","Keep a single source of truth for the model id instead of mixing env vars and provider.json","Lint CI configs for non-anthropic prefixes before deploying"],"tags":["claude","provider","configuration","unsupported-value"],"backgroundTag":"unsupported-config-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}