JuliusBrussee/caveman · error · Error
cave_claude_model_required
Error message
cave_claude_model_required
What it means
resolveClaudeModel() found no model to run: definition.model is not a string, CAVE_MODEL is unset, .caveman/provider.json has no usable model, and ANTHROPIC_API_KEY is absent so even the haiku default cannot be chosen. The Claude harness needs a concrete model id before it can construct the SDK query, so it throws instead of guessing.
Source
Thrown at packages/agent/src/claude-runtime.ts:468
if (/[:\r\n]/.test(name) || /[\r\n]/.test(value)) {
throw new Error("cave_claude_header_invalid");
}
const target = name.toLowerCase();
const kept = (raw ?? "").split(/\r\n|\n|\r/).filter((line) => {
if (!line.trim()) return false;
const colon = line.indexOf(":");
return (colon < 0 ? line : line.slice(0, colon)).trim().toLowerCase() !== target;
});
kept.push(`${name}: ${value}`);
return kept.join("\n");
}
function resolveClaudeModel(definition: AgentDefinition, rootDir: string): string {
const configured = typeof definition.model === "string"
? definition.model
: process.env.CAVE_MODEL ?? localModel(rootDir) ??
(process.env.ANTHROPIC_API_KEY ? "anthropic/claude-haiku-4-5" : undefined);
if (configured === undefined) throw new Error("cave_claude_model_required");
const separator = configured.indexOf("/");
if (separator < 1 || configured.slice(0, separator) !== "anthropic") {
throw new Error("cave_claude_provider_unsupported");
}
return normalizeClaudeModel(configured.slice(separator + 1));
}
function localModel(rootDir: string): string | undefined {
try {
const parsed = JSON.parse(readFileSync(resolve(rootDir, ".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 agent: invalid .caveman/provider.json");
}
}View on GitHub (pinned to 27d5a3981a)
Solutions
- Set model explicitly on the AgentDefinition: { model: "anthropic/claude-haiku-4-5", ... }.
- Or export CAVE_MODEL=anthropic/claude-haiku-4-5 in the environment the agent runs in.
- Or create .caveman/provider.json with { "model": "anthropic/claude-sonnet-4-5" } at the project root.
- Or set ANTHROPIC_API_KEY so the built-in haiku default applies.
Example fix
// before
const definition = { id: "triage", /* no model */ };
// after
const definition = { id: "triage", model: "anthropic/claude-haiku-4-5" }; Defensive patterns
Strategy: validation
Validate before calling
function hasClaudeModel(definition: AgentDefinition, rootDir: string): boolean {
return typeof definition.model === "string"
|| Boolean(process.env.CAVE_MODEL)
|| existsSync(join(rootDir, ".caveman", "provider.json"))
|| Boolean(process.env.ANTHROPIC_API_KEY);
} Type guard
function hasModel(definition: AgentDefinition): definition is AgentDefinition & { model: string } {
return typeof definition.model === "string" && definition.model.length > 0;
} Prevention
- Always set an explicit model on AgentDefinition for the Claude harness — don't rely on the env/file fallback chain.
- In CI, export CAVE_MODEL or ANTHROPIC_API_KEY explicitly so resolution never depends on ambient state.
- Keep .caveman/provider.json checked in for projects that pin a default model locally.
When it happens
Trigger: Calling the Claude run path with an AgentDefinition lacking `model`, no CAVE_MODEL in env, no .caveman/provider.json (or one without a string `model` field), and no ANTHROPIC_API_KEY set — the final ternary yields undefined and the guard fires.
Common situations: Fresh clone without env setup; CI job that intentionally omits ANTHROPIC_API_KEY to avoid spend but still routes a build to the Claude harness; provider.json present but with model set to a non-string (number/object) or misspelled key.
Related errors
- cave_claude_provider_unsupported
- cave_claude_output_budget_too_small_for_reasoning
- cave_claude_reasoning_capability_unknown:${model}
- cave_vercel_terminal_failure
- cave_breaker_threshold_invalid
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/88cce697c87ea53c.
Report an issue: GitHub.