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.
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
- base URL accessibility test failed
- invalid base URL format
- supplier configuration not found
- supplier name, base URL, and API key are required
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/38f2a2a5270bd15f.
Report an issue: GitHub.
Appendix: 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)