{"record":{"id":"c9ce5e008497fde3","repo":"JuliusBrussee/caveman","slug":"caveman-build-set-cave-model-when-zero-or-multipl","errorCode":null,"errorMessage":"caveman build: set CAVE_MODEL when zero or multiple provider credentials exist","messagePattern":"caveman build: set CAVE_MODEL when zero or multiple provider credentials exist","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/cli.ts","lineNumber":1371,"sourceCode":"\nfunction localProviderModel(root: string): string | undefined {\n  try {\n    const parsed = JSON.parse(readFileSync(resolve(root, \".caveman/provider.json\"), \"utf8\")) as { model?: unknown };\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 build: invalid .caveman/provider.json\");\n  }\n}\n\nfunction detectedModel(): string {\n  const configured = [\n    process.env.ANTHROPIC_API_KEY && \"anthropic/claude-haiku-4-5\",\n    process.env.OPENAI_API_KEY && \"openai/gpt-5.4-mini\",\n    (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) && \"google/gemini-2.5-flash\",\n  ].filter((value): value is string => typeof value === \"string\");\n  if (configured.length !== 1) {\n    throw new Error(\"caveman build: set CAVE_MODEL when zero or multiple provider credentials exist\");\n  }\n  return configured[0]!;\n}\n\nasync function loadAgent(path: string): Promise<AgentDefinition> {\n  return agentFromImported(await importFresh(path));\n}\n\nfunction agentFromImported(imported: unknown): AgentDefinition {\n  const exported = imported as { default?: AgentDefinition; agent?: AgentDefinition };\n  const definition = exported.default ?? exported.agent;\n  if (!definition || definition.kind !== \"agent\") throw new Error(\"caveman agent: entry must export default agent()\");\n  return definition;\n}\n\nasync function readLock(root: string): Promise<CaveBuildLock> {\n  return parseCaveBuildLock(JSON.parse(await readFile(resolve(root, \".caveman/agent.lock.json\"), \"utf8\")));\n}","sourceCodeStart":1353,"sourceCodeEnd":1389,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/cli.ts#L1353-L1389","documentation":"detectedModel() in packages/agent/src/cli.ts maps exactly one present provider credential (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GEMINI/GOOGLE_API_KEY) to a default model. If the filtered list does not contain exactly one entry — zero keys or two or more keys — it throws 'caveman build: set CAVE_MODEL when zero or multiple provider credentials exist' because the default model would be ambiguous.","triggerScenarios":"Running 'caveman build' with none of the provider env vars set, or with two or more of ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY/GOOGLE_API_KEY exported simultaneously, without CAVE_MODEL set.","commonSituations":"A shell profile exporting several SDK keys at once (e.g. both Anthropic and OpenAI keys for other tools); CI environment with no credentials at all; recently signed up for a new provider so a second key appeared in the environment.","solutions":["Set CAVE_MODEL to the desired model (e.g. export CAVE_MODEL=anthropic/claude-haiku-4-5) before running caveman build.","Unset the extra provider keys you do not want build to use (e.g. unset OPENAI_API_KEY) so exactly one credential remains.","Export at least one provider key if none is set (e.g. export ANTHROPIC_API_KEY=sk-...).","In CI, add the single intended provider key — or CAVE_MODEL — to the job's environment secrets.","Check `env | grep -E 'ANTHROPIC|OPENAI|GEMINI|GOOGLE_API_KEY'` to see which keys are active in the current shell."],"exampleFix":"# before: two keys exported, ambiguous\nexport ANTHROPIC_API_KEY=sk-ant-...\nexport OPENAI_API_KEY=sk-...\ncaveman build  # throws\n# after\nexport CAVE_MODEL=openai/gpt-5.4-mini\ncaveman build","handlingStrategy":"validation","validationCode":"const keys = [\"ANTHROPIC_API_KEY\", \"OPENAI_API_KEY\", \"GEMINI_API_KEY\", \"GOOGLE_API_KEY\"];\nconst present = keys.filter((k) => process.env[k]);\nif (present.length !== 1 && !process.env.CAVE_MODEL) {\n  throw new Error(`Set CAVE_MODEL or export exactly one provider key (found: ${present.join(\", \") || \"none\"})`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await build();\n} catch (e) {\n  if (e.message.includes(\"set CAVE_MODEL\")) {\n    console.error(\"Exactly one provider credential must be set, or set CAVE_MODEL explicitly.\");\n  } else throw e;\n}","preventionTips":["Set CAVE_MODEL in your shell profile or CI so detection never has to guess.","Keep only the provider key you intend to use in build environments.","Audit the environment with env | grep _API_KEY before running caveman build in CI."],"tags":["cli","environment","configuration"],"backgroundTag":"missing-env-var","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"}