JuliusBrussee/caveman · error · Error
caveman build: set CAVE_MODEL when zero or multiple provider
Error message
caveman build: set CAVE_MODEL when zero or multiple provider credentials exist
What it means
Thrown by `detectedModel()` when auto-detecting a provider model from environment credentials. It collects candidates from exactly one key per provider (`ANTHROPIC_API_KEY` → claude-haiku-4-5, `OPENAI_API_KEY` → gpt-5.4-mini, `GEMINI_API_KEY`/`GOOGLE_API_KEY` → gemini-2.5-flash) and requires exactly one provider to be credentialed. Zero keys or two-plus keys set makes auto-detection ambiguous, so the build refuses and demands an explicit `CAVE_MODEL`.
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 27d5a3981a)
Solutions
- Set `CAVE_MODEL=provider/model-id` explicitly (e.g. `anthropic/claude-haiku-4-5`).
- Or export exactly one provider's key and unset the others for the build shell.
- Or pin `allowedModels` in `caveman.config.ts` so candidate generation never consults credential detection.
- Check for duplicate injection: CI often sets keys via both secret store and job env.
Example fix
# before: zero or multiple credentials export ANTHROPIC_API_KEY=... OPENAI_API_KEY=... npm run build # Error # after export CAVE_MODEL=anthropic/claude-haiku-4-5 npm run build
Defensive patterns
Strategy: validation
Validate before calling
function credentialsUnambiguous(): boolean {
const providers = [
!!process.env.ANTHROPIC_API_KEY,
!!process.env.OPENAI_API_KEY,
!!(process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY),
].filter(Boolean).length;
return providers === 1 || !!process.env.CAVE_MODEL;
} Try / catch
try {
await build(args);
} catch (error) {
if (error instanceof Error && error.message.includes("set CAVE_MODEL")) {
// export CAVE_MODEL=provider/model or reduce to exactly one provider credential
} else throw error;
} Prevention
- Set CAVE_MODEL explicitly in CI and any shell with multiple provider keys exported.
- Scope provider secrets per-job instead of exporting everything everywhere.
- Pin allowedModels in caveman.config.ts to remove env-based detection entirely.
When it happens
Trigger: Running `caveman build` with none of the three provider keys exported (bare CI box), or with several exported at once (a dev shell with both `ANTHROPIC_API_KEY` and `OPENAI_API_KEY`), while `allowedModels` is unset in the config so the CLI falls back to credential-based detection.
Common situations: Dotfiles exporting all available keys; CI secrets injected for multiple providers; switching laptops where stale keys remain exported; containers with no credentials at all.
Related errors
- cave_sandbox_conformance_failed
- caveman build: invalid .caveman/provider.json
- cave_harness_aborted
- cave_vercel_terminal_failure
- caveman build: dataResidency is not enforced yet; refusing t
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/c9ce5e008497fde3.
Report an issue: GitHub.