{"record":{"id":"831db31402b0dcab","repo":"JuliusBrussee/caveman","slug":"unsupported-provider-json-stringify-value","errorCode":null,"errorMessage":"unsupported provider ${JSON.stringify(value)}","messagePattern":"unsupported provider (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/create-caveman-agent/src/index.ts","lineNumber":302,"sourceCode":"npm run check\n\\`\\`\\`\n\nLocal results are inferred. Verified savings remain $0 until active real\ntraffic passes existing Caveman rollout and ledger gates.\n`,\n  };\n}\n\nfunction credentialName(provider: Provider): string {\n  if (provider === \"anthropic\") return \"ANTHROPIC_API_KEY\";\n  if (provider === \"openai\") return \"OPENAI_API_KEY\";\n  return \"GEMINI_API_KEY (or GOOGLE_API_KEY)\";\n}\n\nfunction parseProvider(value: string): Provider {\n  const normalized = value.trim().toLowerCase();\n  if (normalized === \"anthropic\" || normalized === \"openai\" || normalized === \"google\") return normalized;\n  throw new Error(`unsupported provider ${JSON.stringify(value)}`);\n}\n\nfunction safeName(value: string): string {\n  const normalized = value.toLowerCase().replace(/[^a-z0-9_-]+/g, \"-\").replace(/^-+|-+$/g, \"\");\n  if (!normalized) throw new Error(\"project name must contain a letter or number\");\n  return normalized.slice(0, 96);\n}\n\nasync function assertAbsent(path: string): Promise<void> {\n  try {\n    await stat(path);\n    throw new Error(`target already exists: ${path}`);\n  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n  }\n}\n\nmain().catch((error) => {","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/create-caveman-agent/src/index.ts#L284-L320","documentation":"parseProvider normalizes (trim + lowercase) its input and accepts exactly 'anthropic', 'openai', or 'google'. Anything else — including values passed via --provider or typed at the interactive prompt — throws with the JSON.stringify'd original value so whitespace or case surprises are visible. This runs before any scaffold files are written.","triggerScenarios":"`--provider Azure`, `--provider anthropic ` with smart quotes, `--provider gemini` (the accepted name is 'google'), or answering 'claude' at the interactive prompt.","commonSituations":"Using the model-vendor name instead of the provider key (gemini vs google, claude vs anthropic), trailing whitespace from copy-paste, or non-ASCII quotes introduced by rich-text editors/chat apps.","solutions":["Use one of the three canonical values: anthropic, openai, google (case-insensitive).","For Gemini, pass --provider google, not gemini.","Check the echoed JSON in the error for hidden whitespace or quote characters and retype the flag."],"exampleFix":"# before\nnpm create @caveman-ai/agent@latest myapp --provider gemini\n# after\nnpm create @caveman-ai/agent@latest myapp --provider google","handlingStrategy":"type-guard","validationCode":"const PROVIDERS = new Set([\"anthropic\", \"openai\", \"google\"]);\nfunction isProvider(v: string): boolean {\n  return PROVIDERS.has(v.trim().toLowerCase());\n}","typeGuard":"type Provider = \"anthropic\" | \"openai\" | \"google\";\nfunction isProvider(value: string): value is Provider {\n  return [\"anthropic\", \"openai\", \"google\"].includes(value.trim().toLowerCase());\n}","tryCatchPattern":null,"preventionTips":["Use the canonical keys: anthropic, openai, google (gemini is NOT accepted).","Type flags as Provider in TS and validate before spawning the initializer.","Type provider values by hand; avoid copy-paste from rich text that swaps quote characters."],"tags":["cli","validation","provider"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}