{"record":{"id":"f12a830787f07f84","repo":"JuliusBrussee/caveman","slug":"caveman-agent-pinned-model-must-use-provider-mode","errorCode":null,"errorMessage":"caveman agent: pinned model must use provider/model format","messagePattern":"caveman agent: pinned model must use provider/model format","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":3244,"sourceCode":"  }\n  // Runtime accepts GOOGLE_API_KEY as alias for Pi's GEMINI_API_KEY-only\n  // Google provider. Use only after Pi failed to prove a stronger regime, so a\n  // future OAuth-capable Google provider cannot be relabeled as metered.\n  if (provider === \"google\" && process.env.GOOGLE_API_KEY) return \"metered\";\n  return \"unknown\";\n}\n\nfunction resolveModel(definition: AgentDefinition, models: Models, rootDir: string): Model<Api> {\n  if (typeof definition.model === \"object\" && \"provider\" in definition.model) {\n    return definition.model;\n  }\n  const requested = typeof definition.model === \"string\"\n    ? definition.model\n    : process.env.CAVE_MODEL ?? localProviderModel(rootDir);\n  if (requested) {\n    const slash = requested.indexOf(\"/\");\n    if (slash <= 0 || slash === requested.length - 1) {\n      throw new Error(\"caveman agent: pinned model must use provider/model format\");\n    }\n    const model = models.getModel(requested.slice(0, slash), requested.slice(slash + 1));\n    if (!model) throw new Error(`caveman agent: unknown model ${JSON.stringify(requested)}`);\n    return model;\n  }\n\n  const configured: Array<[string, string]> = [];\n  if (process.env.ANTHROPIC_API_KEY) configured.push([\"anthropic\", \"claude-haiku-4-5\"]);\n  if (process.env.OPENAI_API_KEY) configured.push([\"openai\", \"gpt-5.4-mini\"]);\n  if (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) configured.push([\"google\", \"gemini-2.5-flash\"]);\n  if (configured.length !== 1) {\n    throw new Error(\n      configured.length === 0\n        ? \"caveman agent: no supported provider credential found\"\n        : \"caveman agent: multiple provider credentials found; set CAVE_MODEL once\",\n    );\n  }\n  const [provider, id] = configured[0]!;","sourceCodeStart":3226,"sourceCodeEnd":3262,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/runtime.ts#L3226-L3262","documentation":"Model resolution in the agent runtime validates any pinned model string (from `definition.model`, `CAVE_MODEL`, or `.caveman/provider.json`) against `provider/model` format. It throws this error when the string has no `/` at a valid position — either missing entirely, at index 0, or as the last character — because the runtime splits on the first `/` to look up provider and model id.","triggerScenarios":"Setting `CAVE_MODEL=gpt-4o` (no slash), `CAVE_MODEL=/gpt-4o` (leading slash), or `CAVE_MODEL=openai/` (trailing slash); likewise a `definition.model` or `provider.json` `model` field with the same shapes.","commonSituations":"Copy-pasting just a model id from provider docs into `CAVE_MODEL`; editing `.caveman/provider.json` by hand and dropping the provider prefix; shell quoting issues stripping part of the value.","solutions":["Set the value to full `provider/model` form, e.g. `CAVE_MODEL=openai/gpt-5.4-mini` or `anthropic/claude-haiku-4-5`.","Fix `.caveman/provider.json` so `model` includes the provider prefix.","Fix the `model` field on the `AgentDefinition` if pinned there.","Unset `CAVE_MODEL` to fall back to credential-based baseline selection instead of pinning."],"exampleFix":"// before\nCAVE_MODEL=gpt-5.4-mini\n// after\nCAVE_MODEL=openai/gpt-5.4-mini","handlingStrategy":"validation","validationCode":"function isProviderModelFormat(s: string | undefined): boolean {\n  if (!s) return true; // unset falls back to baseline selection\n  const i = s.indexOf(\"/\");\n  return i > 0 && i < s.length - 1;\n}\nif (!isProviderModelFormat(process.env.CAVE_MODEL)) throw new Error(\"CAVE_MODEL must be provider/model\");","typeGuard":"const isPinnedModel = (v: unknown): v is string =>\n  typeof v === \"string\" && /^[^/]+\\/[^/].*$/.test(v) && !v.endsWith(\"/\");","tryCatchPattern":"try {\n  await agent.run(task);\n} catch (e) {\n  if ((e as Error).message.includes(\"pinned model must use provider/model format\")) {\n    console.error(\"Set CAVE_MODEL like openai/gpt-5.4-mini\");\n  } else throw e;\n}","preventionTips":["Always write model pins as `provider/model` with a non-empty provider and model id.","Add a startup env check for CAVE_MODEL format before launching runs.","When copying model ids from provider docs, prepend the provider slug yourself."],"tags":["config","model-selection","format"],"backgroundTag":"invalid-argument-format","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}