{"record":{"id":"d14e3c9acc451d9e","repo":"alibaba/open-code-review","slug":"provider-q-has-no-api-key-or-api-key-cmd-configur","errorCode":null,"errorMessage":"provider %q has no api_key or api_key_cmd configured and no environment variable fallback found","messagePattern":"provider %q has no api_key or api_key_cmd configured and no environment variable fallback found","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/llm/resolver.go","lineNumber":482,"sourceCode":"\t}\n\n\t// Ambient auth follows the protocol actually in force, which is why this is\n\t// resolved after the override above rather than read off the preset. A preset\n\t// declares ambient auth (AmbientAuth), but an entry may override the preset's\n\t// protocol: a bedrock preset switched to \"openai\" speaks a protocol with no\n\t// SigV4 signing and needs a token like anything else. Conversely an entry\n\t// that selects the bedrock protocol explicitly signs its requests whatever\n\t// the preset says.\n\tambientAuth := protocol == ProtocolAnthropicBedrock ||\n\t\t(isPreset && preset.AmbientAuth && entry.Protocol == \"\")\n\n\t// No credential at all is an error, and it is reported before api_key_cmd\n\t// runs: only the command's *execution* is deferred, not the emptiness check.\n\t// An ambient-auth provider is the exception — it has no key to configure,\n\t// since credentials come from the environment's own chain and the request is\n\t// signed rather than bearing a token.\n\tif apiKey == \"\" && apiKeyCmd == \"\" && !ambientAuth {\n\t\treturn ResolvedEndpoint{}, false, fmt.Errorf(\"provider %q has no api_key or api_key_cmd configured and no environment variable fallback found\", cfg.Provider)\n\t}\n\n\tif cfg.Model != \"\" {\n\t\tmodel = cfg.Model\n\t}\n\tif entry.Model != \"\" {\n\t\tmodel = entry.Model\n\t}\n\n\t// Build available model list for validation.\n\tvar availableModels []string\n\tif isPreset {\n\t\tavailableModels = append(availableModels, preset.Models...)\n\t}\n\tavailableModels = append(availableModels, entry.Models...)\n\n\t// A preset's Models list doubles as an allowlist for --model. For an\n\t// ambient-auth provider it cannot: Bedrock identifiers are scoped to an","sourceCodeStart":464,"sourceCodeEnd":500,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/llm/resolver.go#L464-L500","documentation":"Endpoint resolution requires a credential: a static `api_key`, an `api_key_cmd` command that produces one, or a preset provider's environment-variable fallback (e.g. ANTHROPIC_API_KEY). The only exception is ambient-auth providers (e.g. bedrock), where requests are SigV4-signed from the environment's own AWS credential chain. This error fires before api_key_cmd is executed, so it means the config is empty of all credential sources for a non-ambient provider.","triggerScenarios":"Resolving a provider entry where: api_key is empty/whitespace, api_key_cmd is empty/whitespace, no preset env var is set in the environment (custom providers get no env fallback at all), and the effective protocol is not bedrock/ambient-auth.","commonSituations":"Fresh checkout where the API key env var was never exported; running under CI/systemd where the interactive shell env is absent; using a custom provider (no env fallback exists by design) without a key; whitespace-only api_key values which are deliberately treated as unset.","solutions":["Set api_key in the provider entry in the ocr config file","Or set api_key_cmd to a command that prints the key (e.g. a secrets-manager CLI)","For preset providers, export the expected env var (e.g. export ANTHROPIC_API_KEY=sk-...) in the shell/service environment","If using AWS Bedrock, set protocol to anthropic-bedrock (or use a bedrock preset without a protocol override) so ambient AWS auth applies"],"exampleFix":"// before\n[providers.deepseek]\nprotocol = \"openai\"\nurl = \"https://api.deepseek.com\"\n\n// after\n[providers.deepseek]\nprotocol = \"openai\"\nurl = \"https://api.deepseek.com\"\napi_key_cmd = \"op read 'op://Vault/deepseek/key'\"","handlingStrategy":"validation","validationCode":"// Go: pre-flight credential check before invoking ocr\nfunc hasCredential(apiKey, apiKeyCmd, envVar string) error {\n\tif strings.TrimSpace(apiKey) != \"\" || strings.TrimSpace(apiKeyCmd) != \"\" {\n\t\treturn nil\n\t}\n\tif envVar != \"\" && strings.TrimSpace(os.Getenv(envVar)) != \"\" {\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"no credential: set api_key, api_key_cmd, or %s\", envVar)\n}","typeGuard":"func credentialConfigured(apiKey, apiKeyCmd string, envVars ...string) bool {\n\tif strings.TrimSpace(apiKey) != \"\" || strings.TrimSpace(apiKeyCmd) != \"\" {\n\t\treturn true\n\t}\n\tfor _, e := range envVars {\n\t\tif strings.TrimSpace(os.Getenv(e)) != \"\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}","tryCatchPattern":"ep, ok, err := resolver.TryProviderConfig(cfg, \"\")\nif err != nil && strings.Contains(err.Error(), \"no api_key or api_key_cmd\") {\n\tfmt.Fprintln(os.Stderr, \"credential missing; run 'ocr config' or export the provider's API key env var\")\n\tos.Exit(2)\n}","preventionTips":["Export the preset's env var (ANTHROPIC_API_KEY, etc.) in shell profiles and CI secrets","Prefer api_key_cmd over a literal api_key so secrets never sit in config files","Remember custom providers get no env fallback — always set api_key or api_key_cmd for them","Treat whitespace-only key values as missing; re-enter the key if in doubt"],"tags":["config","authentication","llm-provider"],"backgroundTag":"missing-api-key","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}