JuliusBrussee/caveman · error
caveman build: set CAVE_MODEL when zero or multiple…
Error message
caveman build: set CAVE_MODEL when zero or multiple provider credentials exist
What it means
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.
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.
Example fix
# before: two keys exported, ambiguous export ANTHROPIC_API_KEY=sk-ant-... export OPENAI_API_KEY=sk-... caveman build # throws # after export CAVE_MODEL=openai/gpt-5.4-mini caveman build
Defensive patterns
Strategy: validation
Validate before calling
const keys = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY", "GOOGLE_API_KEY"];
const present = keys.filter((k) => process.env[k]);
if (present.length !== 1 && !process.env.CAVE_MODEL) {
throw new Error(`Set CAVE_MODEL or export exactly one provider key (found: ${present.join(", ") || "none"})`);
} Try / catch
try {
await build();
} catch (e) {
if (e.message.includes("set CAVE_MODEL")) {
console.error("Exactly one provider credential must be set, or set CAVE_MODEL explicitly.");
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- multiple provider credentials detected; pass --provider
- no provider credential detected; pass --provider
- CAVE_SETUP_TIMEOUT must be a positive integer
- caveman agent: multiple provider credentials found; set…
- Claude usage refresh needs CAVEMAN_CLAUDE_USAGE_JSON or…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/c9ce5e008497fde3.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/cli.ts:1371
function localProviderModel(root: string): string | undefined {
try {
const parsed = JSON.parse(readFileSync(resolve(root, ".caveman/provider.json"), "utf8")) as { model?: unknown };
return typeof parsed.model === "string" ? parsed.model : undefined;
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
throw new Error("caveman build: invalid .caveman/provider.json");
}
}
function detectedModel(): string {
const configured = [
process.env.ANTHROPIC_API_KEY && "anthropic/claude-haiku-4-5",
process.env.OPENAI_API_KEY && "openai/gpt-5.4-mini",
(process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) && "google/gemini-2.5-flash",
].filter((value): value is string => typeof value === "string");
if (configured.length !== 1) {
throw new Error("caveman build: set CAVE_MODEL when zero or multiple provider credentials exist");
}
return configured[0]!;
}
async function loadAgent(path: string): Promise<AgentDefinition> {
return agentFromImported(await importFresh(path));
}
function agentFromImported(imported: unknown): AgentDefinition {
const exported = imported as { default?: AgentDefinition; agent?: AgentDefinition };
const definition = exported.default ?? exported.agent;
if (!definition || definition.kind !== "agent") throw new Error("caveman agent: entry must export default agent()");
return definition;
}
async function readLock(root: string): Promise<CaveBuildLock> {
return parseCaveBuildLock(JSON.parse(await readFile(resolve(root, ".caveman/agent.lock.json"), "utf8")));
}View on GitHub (pinned to 3ee70a1026)