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

  1. Regenerate/re-copy the API key from the provider dashboard and update the config
  2. Confirm the key belongs to the same provider as the baseUrl (don't mix providers)
  3. Check the key's required header format for the provider (Bearer vs custom)
  4. Remove whitespace/newlines around the key value
  5. 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

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

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/38f2a2a5270bd15f. Report an issue: GitHub.