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

  1. Set `CAVE_MODEL=provider/model-id` explicitly (e.g. `anthropic/claude-haiku-4-5`).
  2. Or export exactly one provider's key and unset the others for the build shell.
  3. Or pin `allowedModels` in `caveman.config.ts` so candidate generation never consults credential detection.
  4. 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

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


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/c9ce5e008497fde3. Report an issue: GitHub.