Billionmail/BillionMail · error

unexpected API response: %d %s

Error message

unexpected API response: %d %s

What it means

When the models endpoint responds with any status other than 200, 401, or 404, testAPIKeyValidity fails with 'unexpected API response: <code> <text>'. Notably, 404 is treated as success (some providers don't expose /models), so this error fires for statuses like 403, 429, 500, 502, 503.

Source

Thrown at core/internal/service/askai/supplier.go:547

	}
	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 {
	supplierConfig, err := ReadSupplierConfig(supplierName)
	if err != nil {
		return errors.New("supplier configuration not found")
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Read the status code in the message and act on it specifically (403 → permissions, 429 → backoff, 5xx → retry later)
  2. Check the provider's status page for outages
  3. Verify account permissions to access the models endpoint
  4. Wait and retry with backoff if rate-limited
  5. Confirm the baseUrl points to the correct API gateway, not a proxy that masks errors

Example fix

// before
err := askai.Testing(name, url, key) // hit 429 in a retry loop
// after
for i := 0; i < 3; i++ {
    err = askai.Testing(name, url, key)
    if err == nil || !strings.Contains(err.Error(), "429") { break }
    time.Sleep(time.Duration(1<<i) * time.Second)
}
Defensive patterns

Strategy: retry

Try / catch

var err error
for attempt := 0; attempt < 3; attempt++ {
    err = askai.Testing(name, baseUrl, apiKey)
    if err == nil || !strings.Contains(err.Error(), "unexpected API response") { break }
    time.Sleep(time.Duration(1<<attempt) * time.Second)
}
if err != nil {
    return fmt.Errorf("provider returned an unexpected status; check provider status page: %w", err)
}

Prevention

When it happens

Trigger: GET <baseUrl>/models returns 403 (forbidden), 429 (rate limited), 5xx (server error), or a gateway error while Testing runs.

Common situations: Provider outage or maintenance (5xx); key valid but lacking model-list permission (403); rate limiting during automated test loops (429); reverse proxy returning 502/503; endpoint behind an auth gateway with different error semantics.

Related errors


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