JuliusBrussee/caveman · error
caveman build: invalid .caveman/provider.json
Error message
caveman build: invalid .caveman/provider.json
What it means
localProviderModel in packages/agent/src/cli.ts reads .caveman/provider.json to detect a configured model. If the file parses as JSON but readFileSync fails with a non-ENOENT error, or JSON.parse throws a syntax error, the function throws 'caveman build: invalid .caveman/provider.json'. Only a completely absent file is tolerated (returns undefined).
Solutions
- Validate and repair the JSON: run `node -e "JSON.parse(require('fs').readFileSync('.caveman/provider.json','utf8'))"` and fix syntax errors (or delete the file if you do not need a pinned model).
- Ensure .caveman/provider.json is a regular file, not a directory or symlink to a missing target.
- Fix file permissions so the current user can read it (chmod 644 .caveman/provider.json).
- Set CAVE_MODEL explicitly so provider detection does not depend on this file.
- Remove a stale/truncated provider.json and let the build re-detect the model from provider credentials.
Example fix
// before (invalid JSON)
{ "model": "anthropic/claude-haiku-4-5", }
// after
{ "model": "anthropic/claude-haiku-4-5" } Defensive patterns
Strategy: try-catch
Validate before calling
const fs = require("fs");
if (fs.existsSync(".caveman/provider.json")) {
try { JSON.parse(fs.readFileSync(".caveman/provider.json", "utf8")); }
catch { console.error("provider.json is not valid JSON — fix or delete it"); }
} Try / catch
try {
await build();
} catch (e) {
if (e.message.includes("invalid .caveman/provider.json")) {
console.error("Repair or remove .caveman/provider.json, or set CAVE_MODEL.");
} else throw e;
} Prevention
- Validate .caveman/provider.json with a JSON linter after every manual edit.
- Write the file atomically (write tmp file, rename) to avoid truncated JSON.
- Set CAVE_MODEL explicitly so you never depend on provider.json parsing.
- Ensure the path is a regular readable file, not a directory or mis-modeled symlink.
When it happens
Trigger: Running 'caveman build' when .caveman/provider.json exists but contains invalid JSON (trailing comma, truncated write), or when reading it fails with a permission/IO error (EACCES, EISDIR) rather than ENOENT.
Common situations: A partially written or hand-edited provider.json left invalid JSON behind; the path .caveman/provider.json is actually a directory; restrictive file permissions after copying the repo as another user; editor swap/temp files renamed incorrectly.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- cave_live_eval_sandbox_profile_invalid
- caveman agent: invalid .caveman/provider.json
- eval evidence line is not valid JSON
- eval evidence line must be a JSON object
- already exists and is not Caveman-owned; refusing to…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/64a2bf4f33f0a067.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/cli.ts:1360
};
}
function evalDynamicKinds(evals: readonly EvalDefinition[]): ReadonlySet<ContextKind> {
const kinds = new Set<ContextKind>();
if (evals.some((fixture) => fixture.quality.some((grader) => grader.type === "tool_called"))) {
kinds.add("history");
kinds.add("tool_result");
}
return kinds;
}
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));
}View on GitHub (pinned to 3ee70a1026)