{"record":{"id":"64a2bf4f33f0a067","repo":"JuliusBrussee/caveman","slug":"caveman-build-invalid-caveman-provider-json","errorCode":null,"errorMessage":"caveman build: invalid .caveman/provider.json","messagePattern":"caveman build: invalid \\.caveman/provider\\.json","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/cli.ts","lineNumber":1360,"sourceCode":"  };\n}\n\nfunction evalDynamicKinds(evals: readonly EvalDefinition[]): ReadonlySet<ContextKind> {\n  const kinds = new Set<ContextKind>();\n  if (evals.some((fixture) => fixture.quality.some((grader) => grader.type === \"tool_called\"))) {\n    kinds.add(\"history\");\n    kinds.add(\"tool_result\");\n  }\n  return kinds;\n}\n\nfunction localProviderModel(root: string): string | undefined {\n  try {\n    const parsed = JSON.parse(readFileSync(resolve(root, \".caveman/provider.json\"), \"utf8\")) as { model?: unknown };\n    return typeof parsed.model === \"string\" ? parsed.model : undefined;\n  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n    throw new Error(\"caveman build: invalid .caveman/provider.json\");\n  }\n}\n\nfunction detectedModel(): string {\n  const configured = [\n    process.env.ANTHROPIC_API_KEY && \"anthropic/claude-haiku-4-5\",\n    process.env.OPENAI_API_KEY && \"openai/gpt-5.4-mini\",\n    (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) && \"google/gemini-2.5-flash\",\n  ].filter((value): value is string => typeof value === \"string\");\n  if (configured.length !== 1) {\n    throw new Error(\"caveman build: set CAVE_MODEL when zero or multiple provider credentials exist\");\n  }\n  return configured[0]!;\n}\n\nasync function loadAgent(path: string): Promise<AgentDefinition> {\n  return agentFromImported(await importFresh(path));\n}","sourceCodeStart":1342,"sourceCodeEnd":1378,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/cli.ts#L1342-L1378","documentation":"Thrown by `localProviderModel` when reading `.caveman/provider.json` fails with any error other than ENOENT. A missing file is fine (returns undefined); a file that exists but cannot be parsed as JSON with an optional string `model` — permission denied, invalid JSON, a directory — makes the build fail with this message instead of ignoring local provider override config.","triggerScenarios":"`.caveman/provider.json` exists but contains a syntax error (trailing comma, single quotes, BOM from a Windows editor), is a directory, or has unreadable permissions. Any non-ENOENT filesystem/parse error from `readFileSync`/`JSON.parse` lands here.","commonSituations":"Hand-editing the override file and breaking JSON; tools writing partial files on crash; a checked-in file with comments (JSONC) that JSON.parse rejects; permission changes after a chmod/chown.","solutions":["Fix or delete `.caveman/provider.json` — the safest valid form is `{ \"model\": \"provider/model-id\" }`.","Validate with `node -e 'JSON.parse(require(\"fs\").readFileSync(\".caveman/provider.json\",\"utf8\"))'`.","If you don't need a local override, remove the file entirely (missing is tolerated)."],"exampleFix":"// before: .caveman/provider.json\n{ 'model': 'anthropic/claude-haiku-4-5', }  // single quotes + trailing comma\n\n// after\n{ \"model\": \"anthropic/claude-haiku-4-5\" }","handlingStrategy":"validation","validationCode":"import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nfunction providerOverrideParses(root: string): boolean {\n  try {\n    const parsed = JSON.parse(readFileSync(resolve(root, \".caveman/provider.json\"), \"utf8\"));\n    return parsed == null || typeof parsed.model === \"string\" || parsed.model === undefined;\n  } catch (error) {\n    return (error as NodeJS.ErrnoException).code === \"ENOENT\";\n  }\n}","typeGuard":"function isProviderOverride(v: unknown): v is { model?: string } {\n  return typeof v === \"object\" && v !== null && ((v as { model?: unknown }).model === undefined || typeof (v as { model?: unknown }).model === \"string\");\n}","tryCatchPattern":"try {\n  await build(args);\n} catch (error) {\n  if (error instanceof Error && error.message === \"caveman build: invalid .caveman/provider.json\") {\n    // fix the JSON syntax or delete the file, then retry\n  } else throw error;\n}","preventionTips":["Write the file with JSON.stringify, not editors that add comments or trailing commas.","Validate the file in a prebuild step.","Remember: absent file is fine; broken file is fatal."],"tags":["config","json","provider","build"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}