JuliusBrussee/caveman · error

${configPath} provider must be a JSON object; refusing to ov

Error message

${configPath} provider must be a JSON object; refusing to overwrite it

What it means

opencodeNativeMutations() rewrites provider baseURLs inside ~/.config/opencode/opencode.json. opencode's schema requires `provider` to be a map of provider-id to provider config; if the key exists but is not a JSON object, Caveman throws instead of clobbering it.

Source

Thrown at packages/cli/src/index.ts:6325

    call("PreCompact", { session_id: input.sessionID });
    const stable = sessionContext(input.sessionID);
    if (stable) output.context.push(stable);
  },
  dispose: async () => {
    for (const sessionID of contexts.keys()) call("SessionEnd", { session_id: sessionID });
    contexts.clear();
    pending.clear();
  },
});
`;
}

function opencodeNativeMutations(gw: string, mcpBinary: string): NativeMutation[] {
  const configPath = join(homedir(), ".config", "opencode", "opencode.json");
  const before = fileBytes(configPath);
  const root = parseJsonFileObject(configPath, before);
  if (root.provider !== undefined && (typeof root.provider !== "object" || root.provider === null || Array.isArray(root.provider))) {
    throw new Error(`${configPath} provider must be a JSON object; refusing to overwrite it`);
  }
  if (root.mcp !== undefined && (typeof root.mcp !== "object" || root.mcp === null || Array.isArray(root.mcp))) {
    throw new Error(`${configPath} mcp must be a JSON object; refusing to overwrite it`);
  }
  const providers = root.provider && typeof root.provider === "object" && !Array.isArray(root.provider) ? root.provider as Record<string, unknown> : {};
  const previousRoutes: Record<string, unknown> = {};
  const base = appendUrlPath(gw, "/w/opencode");
  const routes = { openai: appendUrlPath(base, "/openai/v1"), anthropic: appendUrlPath(base, "/anthropic/v1") };
  for (const [providerID, route] of Object.entries(routes)) {
    const provider = providers[providerID] && typeof providers[providerID] === "object" && !Array.isArray(providers[providerID]) ? providers[providerID] as Record<string, unknown> : {};
    const options = provider.options && typeof provider.options === "object" && !Array.isArray(provider.options) ? provider.options as Record<string, unknown> : {};
    previousRoutes[providerID] = options.baseURL ?? null;
    options.baseURL = route;
    provider.options = options;
    providers[providerID] = provider;
  }
  root.provider = providers;
  const mcp = root.mcp && typeof root.mcp === "object" && !Array.isArray(root.mcp) ? root.mcp as Record<string, unknown> : {};

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Rewrite opencode.json so provider is an object keyed by provider id, each with nested options (e.g. { "openai": { "options": { "baseURL": "..." } } })
  2. Or delete the provider key entirely so Caveman installs its entries fresh
  3. Validate with `jq '.provider | type' ~/.config/opencode/opencode.json` then re-run the install

Example fix

// before — ~/.config/opencode/opencode.json
{ "provider": "openai" }

// after
{ "provider": { "openai": { "options": { "baseURL": "https://api.openai.com/v1" } } } }
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from "node:fs";
function opencodeProviderShapeOk(path: string): boolean {
  try {
    const p = JSON.parse(readFileSync(path, "utf8")).provider;
    return p === undefined || (typeof p === "object" && p !== null && !Array.isArray(p));
  } catch { return true; }
}

Type guard

const isJsonObject = (v: unknown): v is Record<string, unknown> =>
  typeof v === "object" && v !== null && !Array.isArray(v);

Try / catch

try { nativeInstallOpencode(); } catch (e) {
  if (e instanceof Error && /provider must be a JSON object/.test(e.message)) {
    rewriteProviderAsMap(); nativeInstallOpencode();
  } else throw e;
}

Prevention

When it happens

Trigger: Enabling native opencode routing when opencode.json contains "provider": "anthropic" (string), an array, null, or a number.

Common situations: Confusion between the `provider` (map of provider definitions) and `model`/provider-id string settings in opencode config; hand-edits from following an outdated opencode doc; config written for a different tool version.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/393277a336cae768. Report an issue: GitHub.