JuliusBrussee/caveman · error
cave_claude_provider_unsupported
cave_claude_provider_unsupported
Error message
cave_claude_provider_unsupported
What it means
resolveClaudeModel resolves the Claude model from the agent definition, CAVE_MODEL env var, .caveman/provider.json, or a default when ANTHROPIC_API_KEY exists. The Claude runtime only accepts models whose provider prefix is exactly "anthropic/" (slash after at least one character). Any other provider prefix triggers this fail-closed guard because this executor only supports Anthropic models.
Solutions
- Prefix the model with "anthropic/", e.g. CAVE_MODEL=anthropic/claude-haiku-4-5 or model: "anthropic/claude-sonnet-4-5" in the definition.
- If you need a non-Anthropic provider, use a different runtime/adapter (Pi runtime or adapters.ts bridges) instead of the Claude executor.
- Check .caveman/provider.json and CAVE_MODEL for a stale provider prefix left by another tool.
Example fix
// before process.env.CAVE_MODEL = "openai/gpt-4o"; // after process.env.CAVE_MODEL = "anthropic/claude-haiku-4-5";
Defensive patterns
Strategy: validation
Validate before calling
function isAnthropicModel(model) {
const i = model.indexOf("/");
return i >= 1 && model.slice(0, i) === "anthropic";
}
const model = definition.model ?? process.env.CAVE_MODEL;
if (model !== undefined && !isAnthropicModel(model)) {
throw new Error(`refusing to run: ${model} is not an anthropic/ model`);
} Type guard
function isAnthropicModel(v) { return typeof v === "string" && v.indexOf("/") >= 1 && v.slice(0, v.indexOf("/")) === "anthropic"; } Try / catch
try {
await runClaudeAgent(options);
} catch (e) {
if (e.message === "cave_claude_provider_unsupported") {
console.error(`Model must be prefixed "anthropic/", got: ${options.model ?? process.env.CAVE_MODEL}`);
} else throw e;
} Prevention
- Always use provider-qualified "anthropic/<model>" ids in CAVE_MODEL and provider.json
- Keep a single source of truth for the model id instead of mixing env vars and provider.json
- Lint CI configs for non-anthropic prefixes before deploying
When it happens
Trigger: Passing a model string like "openai/gpt-4o", "bedrock/...", "openrouter/..." via definition.model, CAVE_MODEL, or .caveman/provider.json; or a string with no "/" at all (indexOf returns -1) or one starting with "/" (separator < 1).
Common situations: Reusing a model id written for a multi-provider gateway (e.g. OpenRouter-style "anthropic/claude-..." is fine but "google/gemini..." is not); copying a provider.json from a project configured for another provider; forgetting the "anthropic/" prefix entirely.
Related errors
- cave_claude_output_budget_too_small_for_reasoning
- cave_claude_reasoning_capability_unknown
- caveman agent: invalid .caveman/provider.json
- aes cipher
- apiKey, baseURL, and agent are required
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/dcae21f662ee14dd.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/claude-runtime.ts:471
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");
}
}
function normalizeClaudeModel(model: string): string {
return model.startsWith("anthropic/") ? model.slice("anthropic/".length) : model;View on GitHub (pinned to 3ee70a1026)