Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions internal/bootstrap/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,11 @@ func resolveInput(opts Options) (*resolvedInput, error) {

func ensureProviderSetup(cwd string, settings config.Resolved, nonTTY bool) (config.Resolved, string, error) {
configExists := config.GlobalConfigExists() || config.ProjectConfigExists(cwd)
if configExists {
if hasConfiguredProviderCredentials(settings, settings.Provider) {
return settings, "", nil
}
return settings, "", fmt.Errorf("configuration error: settings.provider=%q is missing or not configured in settings.json", settings.Provider)
if configExists && hasConfiguredProviderCredentials(settings, settings.Provider) {
return settings, "", nil
}

// Even with a config file, try env var fallback when api_key is empty.
apiKey, _ := settings.ProviderCredentials(settings.Provider)
if apiKey != "" {
return settings, envHintFor(settings), nil
Expand All @@ -112,6 +110,10 @@ func ensureProviderSetup(cwd string, settings config.Resolved, nonTTY bool) (con
return settings, fmt.Sprintf("Using %s from environment", envKey), nil
}

if configExists {
return settings, "", fmt.Errorf("configuration error: settings.provider=%q is missing or not configured in settings.json", settings.Provider)
}

if nonTTY {
return settings, "", fmt.Errorf("api key not set; set %s or configure providers in %s",
config.ProviderEnvKey(settings.Provider), filepath.Join(config.UserConfigDir(), "settings.json"))
Expand Down
10 changes: 8 additions & 2 deletions internal/config/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,17 @@ var providerEnvVars = map[string]struct{ key, base string }{

// ProviderCredentials returns API key and base URL for the given provider.
// It checks the providers map first, then falls back to standard environment variables.
// Config base URL is preserved even when the API key comes from the environment.
func (r Resolved) ProviderCredentials(prov string) (apiKey, baseURL string) {
if pc, ok := r.Providers[prov]; ok && pc.APIKey != "" {
pc, hasConfig := r.Providers[prov]
if hasConfig && pc.APIKey != "" {
return pc.APIKey, pc.BaseURL
}
return EnvCredentials(prov)
apiKey, baseURL = EnvCredentials(prov)
if hasConfig && baseURL == "" {
baseURL = pc.BaseURL
}
return apiKey, baseURL
Comment on lines 124 to +133
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ProviderCredentials calls EnvCredentials, but EnvCredentials only reads env vars for providers listed in providerEnvVars. For custom/free-form provider names this means an API key present in the environment (e.g. derived via ProviderEnvKey) will never be picked up, even though the surrounding UX/error messages imply a generic _API_KEY fallback. Consider updating ProviderCredentials/EnvCredentials to fall back to os.Getenv(ProviderEnvKey(prov)) when the provider isn’t in providerEnvVars (and clarify how custom provider names map to valid env var identifiers).

Copilot uses AI. Check for mistakes.
}

// ProviderEnvKey returns the standard environment variable name for a provider's API key.
Expand Down
Loading