JuliusBrussee/caveman · error
cave_claude_reasoning_capability_unknown
cave_claude_reasoning_capability_unknown
Error message
cave_claude_reasoning_capability_unknown:${model} What it means
claudeThinkingCapability classifies models by regex into "adaptive", "manual", or "unknown". When reasoning is enabled and the model is neither adaptive nor manual (capability "unknown"), the runtime refuses to guess how to configure thinking and throws with the model name embedded, so new/renamed Claude models fail loudly until supported.
Solutions
- Switch to a supported model, e.g. anthropic/claude-haiku-4-5 or claude-sonnet-4-5.
- If reasoning is not required, set the agent's reasoning to "off" (capability check is skipped).
- Upgrade the caveman agent package so its capability table recognizes your model.
- Fix typos in the configured model name.
Example fix
// before model: "anthropic/claude-3-5-sonnet-20241022" // after model: "anthropic/claude-sonnet-4-5"
Defensive patterns
Strategy: validation
Validate before calling
const KNOWN = [/^claude-(?:haiku-4-5|sonnet-4-5|opus-4-(?:1|5))(?:-\d{8})?$/];
function isKnownClaudeModel(model) {
return KNOWN.some((re) => re.test(model));
}
if (reasoning !== "off" && !isKnownClaudeModel(model)) {
throw new Error(`model "${model}" unknown to this runtime; disable reasoning or pick a supported model`);
} Type guard
function isKnownClaudeModel(v) { return typeof v === "string" && /^claude-(?:haiku-4-5|sonnet-4-5|opus-4-(?:1|5))(?:-\d{8})?$/.test(v); } Try / catch
try {
await run(agent, { reasoning: "high" });
} catch (e) {
if (String(e.message).startsWith("cave_claude_reasoning_capability_unknown:")) {
console.error(`Unsupported model for reasoning: ${e.message.split(":")[1]}; falling back to reasoning off`);
return run(agent, { reasoning: "off" });
}
throw e;
} Prevention
- Pin models from the runtime's supported list
- Upgrade the agent package before adopting newly released Claude models
- Catch unknown-model strings early by validating model names at config load time
When it happens
Trigger: Enabling reasoning on a model string that does not match the known patterns, e.g. "claude-3-5-sonnet", "claude-opus-4-6", a dated snapshot with unexpected format, or a typo like "claude-sonet-4-5".
Common situations: Using an older or newer Claude model added after this runtime's capability table was written; misspelling the model name in CAVE_MODEL or provider.json; snapshot date suffix variants not matching the regex.
Related errors
- cave_claude_output_budget_too_small_for_reasoning
- cave_claude_provider_unsupported
- cave_claude_subagent_bridge_unavailable
- cave_eve_usage_missing
- cave_execution_authorization_private
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/05a4f228f548888b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/claude-runtime.ts:518
reasoning: AgentDefinition["reasoning"],
outputMaxTokens: number | undefined,
): Pick<ClaudeSDKOptions, "thinking" | "effort"> {
if (reasoning === "off") return { thinking: { type: "disabled" } };
const capability = claudeThinkingCapability(model);
if (capability === "adaptive") {
return {
thinking: { type: "adaptive" },
effort: claudeEffort(reasoning),
};
}
if (capability === "manual") {
const budgetTokens = reasoning === "high" ? 8_192 : reasoning === "medium" ? 4_096 : 1_024;
if (outputMaxTokens !== undefined && outputMaxTokens <= budgetTokens) {
throw new Error("cave_claude_output_budget_too_small_for_reasoning");
}
return { thinking: { type: "enabled", budgetTokens } };
}
throw new Error(`cave_claude_reasoning_capability_unknown:${model}`);
}
function claudeThinkingCapability(model: string): "adaptive" | "manual" | "unknown" {
if (/^claude-(?:haiku-4-5|sonnet-4-5|opus-4-(?:1|5))(?:-\d{8})?$/.test(model)) {
return "manual";
}
if (/^claude-(?:sonnet|opus)-4-[678](?:-\d{8})?$/.test(model) ||
/^claude-(?:fable|mythos|sonnet|opus)-5(?:-\d+)?$/.test(model)) {
return "adaptive";
}
return "unknown";
}
// Accepts ANY result subtype: error subtypes (error_max_turns, …) carry the
// same provider `usage` a success does, so the receipt can be built from a
// failed run too.
type ClaudeCredentialRegime = "metered" | "subscription" | "unknown";
View on GitHub (pinned to 3ee70a1026)