{"record":{"id":"00498c795da3e477","repo":"JuliusBrussee/caveman","slug":"caveman-code-model-must-use-provider-model-format","errorCode":null,"errorMessage":"caveman-code: model must use provider/model format","messagePattern":"caveman-code: model must use provider/model format","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/code.ts","lineNumber":551,"sourceCode":"      exitCode = code;\n      finish();\n    });\n    // `close` waits for stdio EOF, which a surviving background descendant never\n    // gives. `exit` is the command's own answer, so the run settles on it with\n    // whatever output arrived rather than waiting on a process it does not own.\n    child.once(\"exit\", (code) => {\n      exitCode = code;\n      setTimeout(finish, EXIT_FLUSH_GRACE_MS).unref();\n    });\n  });\n}\n\nfunction resolveCodingModelID(explicit: string | undefined): string {\n  const requested = explicit ?? process.env.CAVE_MODEL;\n  if (requested !== undefined && requested !== \"\") {\n    const slash = requested.indexOf(\"/\");\n    if (slash <= 0 || slash === requested.length - 1) {\n      throw new Error(\"caveman-code: model must use provider/model format\");\n    }\n    return requested;\n  }\n  const configured: string[] = [];\n  if (process.env.ANTHROPIC_API_KEY) configured.push(\"anthropic/claude-sonnet-4-6\");\n  if (process.env.OPENAI_API_KEY) configured.push(\"openai/gpt-5.5\");\n  if (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) {\n    configured.push(\"google/gemini-2.5-pro\");\n  }\n  if (configured.length !== 1) {\n    throw new Error(\n      configured.length === 0\n        ? \"caveman-code: no supported provider credential found; set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY\"\n        : \"caveman-code: multiple provider credentials found; set CAVE_MODEL to pick one\",\n    );\n  }\n  return configured[0]!;\n}","sourceCodeStart":533,"sourceCodeEnd":569,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/code.ts#L533-L569","documentation":"A model identifier given explicitly (argument) or via CAVE_MODEL must be in provider/model form: exactly one '/' that is neither the first nor the last character. 'claude-sonnet', '/model', 'provider/', and 'a/b/c' style values with slash at index 0 or the end are rejected. Note the check only pins slash position, so 'a/b/c' passes; validate your own IDs if you need stricter shapes.","triggerScenarios":"Setting CAVE_MODEL=claude-sonnet-4-6 (no provider), CAVE_MODEL=/gpt-5.5, CAVE_MODEL=anthropic/ (no model), or passing such a value as the explicit model argument to the coding session.","commonSituations":"Copy-pasting a bare model name from provider docs; CAVE_MODEL set to an empty-suffix value by a CI variable; using a vendor-specific ID format (e.g. OpenAI model names have no slash).","solutions":["Format the ID as provider/model, e.g. CAVE_MODEL=anthropic/claude-sonnet-4-6 or openai/gpt-5.5","Check for stray whitespace or a trailing slash in the env var: `printf '%s' \"$CAVE_MODEL\" | od -c`","Unset CAVE_MODEL entirely to let the credential-based default resolution pick a model"],"exampleFix":"# before\nexport CAVE_MODEL=\"claude-sonnet-4-6\"\n\n# after\nexport CAVE_MODEL=\"anthropic/claude-sonnet-4-6\"","handlingStrategy":"validation","validationCode":"function isValidModelID(id: string): boolean {\n  const slash = id.indexOf(\"/\");\n  return slash > 0 && slash === id.lastIndexOf(\"/\") && slash < id.length - 1;\n}\n\nconst requested = process.env.CAVE_MODEL;\nif (requested !== undefined && requested !== \"\" && !isValidModelID(requested)) {\n  throw new Error(`CAVE_MODEL must be provider/model, got: ${requested}`);\n}","typeGuard":"function isProviderModelID(v: unknown): v is string {\n  return typeof v === \"string\" && /^[^/\\s]+\\/[^/\\s]+$/.test(v) && !v.includes(\"//\");\n}","tryCatchPattern":null,"preventionTips":["Validate CAVE_MODEL at startup, not at first session creation","Document the provider/model format next to every place CAVE_MODEL is set (docker-compose, CI yaml)"],"tags":["config","model-id","env","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}