Billionmail/BillionMail · error
invalid API key
Error message
invalid API key
What it means
Inside testAPIKeyValidity, a 401 Unauthorized response from the provider's models endpoint is mapped to the literal error 'invalid API key'. It means the request reached the provider but the credentials were rejected.
Source
Thrown at core/internal/service/askai/supplier.go:543
client := &http.Client{Timeout: 15 * time.Second}
req, err := http.NewRequest("GET", apiUrl, nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusOK:
return nil // API key is successfully validated
case http.StatusUnauthorized:
return errors.New("invalid API key")
case http.StatusNotFound:
return nil
default:
return fmt.Errorf("unexpected API response: %d %s",
resp.StatusCode, http.StatusText(resp.StatusCode))
}
}
// GetSupplierConfig retrieves the configuration for a specific supplier by its name.
// It reads the configuration file and returns a Supplier struct containing the supplier's details.
// If the configuration file does not exist or an error occurs, it returns an error.
func GetSupplierConfig(supplierName string) (*Supplier, error) {
return ReadSupplierConfig(supplierName)
}
// SetSupplierStatus updates the status of a supplier in its configuration file.
// It reads the existing configuration, modifies the status, and saves it back to the file.
func SetSupplierStatus(supplierName string, status bool) error {View on GitHub (pinned to fc36c76c05)
Solutions
- Regenerate/re-copy the API key from the provider dashboard and update the config
- Confirm the key belongs to the same provider as the baseUrl (don't mix providers)
- Check the key's required header format for the provider (Bearer vs custom)
- Remove whitespace/newlines around the key value
- Verify the key has not expired or been revoked
Example fix
// before
cfg.ApiKey = os.Getenv("KEY") // value had trailing newline
// after
cfg.ApiKey = strings.TrimSpace(os.Getenv("KEY"))
err := askai.Testing(name, cfg.BaseUrl, cfg.ApiKey) Defensive patterns
Strategy: validation
Validate before calling
func keyFormatLooksValid(key string) bool {
k := strings.TrimSpace(key)
// OpenAI-style keys start with sk- and have no whitespace
return k != "" && !strings.ContainsAny(k, " \n\t\r") && len(k) >= 20
} Try / catch
if err := askai.Testing(name, baseUrl, apiKey); err != nil {
if err.Error() == "invalid API key" {
return fmt.Errorf("the provider rejected the API key (401); regenerate it and update config")
}
return err
} Prevention
- Store keys in a secret manager, not hardcoded or hand-typed
- Rotate and re-verify keys after any provider-side rotation
- Match key to provider endpoint exactly
- Strip surrounding whitespace when loading keys from env or files
When it happens
Trigger: testAPIKeyValidity receives HTTP 401 from GET <baseUrl>/models — the key is wrong, revoked, expired, or formatted incorrectly for the Authorization header.
Common situations: Typo or truncation when pasting the key; key belongs to a different provider than the baseUrl; key was rotated/revoked; missing or wrongly prefixed auth header ('Bearer ' vs 'Api-Key ') for a non-OpenAI-compatible provider.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- API key validation failed: %w
- supplier configuration not found
- supplier name, base URL, and API key are required
- invalid base URL format: %w
- base URL accessibility test failed: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/38f2a2a5270bd15f.
Report an issue: GitHub.