JuliusBrussee/caveman · error · Error
caveman agent: invalid .caveman/provider.json
Error message
caveman agent: invalid .caveman/provider.json
What it means
localModel() reads <rootDir>/.caveman/provider.json to find a locally configured model. ENOENT (file absent) is fine and returns undefined, but any other failure — JSON.parse syntax errors, EACCES, EISDIR — rethrows as "caveman agent: invalid .caveman/provider.json". A present-but-corrupt provider file is treated as a hard configuration defect, not silently ignored.
Source
Thrown at packages/agent/src/claude-runtime.ts:484
: 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;
}
function claudeEffort(reasoning: AgentDefinition["reasoning"]): "low" | "medium" | "high" {
if (reasoning === "medium") return "medium";
if (reasoning === "high") return "high";
return "low";
}
function claudeReasoningOptions(
model: string,
reasoning: AgentDefinition["reasoning"],
outputMaxTokens: number | undefined,
): Pick<ClaudeSDKOptions, "thinking" | "effort"> {View on GitHub (pinned to 27d5a3981a)
Solutions
- Fix the JSON: cat .caveman/provider.json | jq . to find the syntax error, then correct it.
- Ensure the file is a plain object with a string model: { "model": "anthropic/claude-sonnet-4-5" }.
- If you don't want local provider config, delete the file entirely — absence is handled gracefully.
- Make writers atomic (write temp + rename) so crashes can't leave partial files.
Example fix
// before: .caveman/provider.json
{ "model": "anthropic/claude-sonnet-4-5", } // trailing comma → JSON.parse throws
// after
{ "model": "anthropic/claude-sonnet-4-5" } Defensive patterns
Strategy: validation
Validate before calling
function providerModelOrNull(rootDir: string): string | null {
try {
const parsed = JSON.parse(readFileSync(join(rootDir, ".caveman", "provider.json"), "utf8"));
return typeof parsed?.model === "string" ? parsed.model : null;
} catch {
return null; // treat unreadable/invalid as absent at YOUR layer; decide explicitly
}
} Type guard
function isValidProviderJson(value: unknown): value is { model: string } {
return typeof value === "object" && value !== null
&& typeof (value as { model?: unknown }).model === "string";
} Prevention
- Validate .caveman/provider.json with `jq .` after every hand edit.
- Write config files atomically (tmp file + rename) so crashes never leave truncated JSON.
- Remember ENOENT is fine but any other read/parse failure is fatal — delete the file rather than leaving it corrupt.
When it happens
Trigger: provider.json exists but contains invalid JSON (trailing comma, single quotes, comment, BOM, truncated by a concurrent write), is a directory, or is unreadable due to permissions; then a Claude-lane model resolution runs and reaches localModel().
Common situations: Hand-editing provider.json and leaving a trailing comma; a tool writing the file non-atomically so a crash leaves it half-written; committing a template with placeholder text; file owned by another user in shared CI.
Related errors
- cave_vercel_terminal_failure
- ${envVar} does not point to an executable: ${explicit}
- cache-replay: verifier command must be an existing regular f
- cave_harness_aborted
- cave_eve_terminal_${result.status}
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/f7b9f78612946a2e.
Report an issue: GitHub.