This commit is contained in:
2026-08-23 13:08:14 +02:00
parent ee1405cc1a
commit 89ae7e5fa8
4 changed files with 74 additions and 7 deletions
@@ -50,6 +50,7 @@ func formatOpenAIHTTPError(status int, errObj *openAIErrorBody) string {
lower := strings.ToLower(msg)
codeLower := strings.ToLower(code)
paramLower := strings.ToLower(param)
typLower := strings.ToLower(typ)
switch {
case status == 401 || codeLower == "invalid_api_key" ||
@@ -58,11 +59,20 @@ func formatOpenAIHTTPError(status int, errObj *openAIErrorBody) string {
return "AI provider rejected the API key"
case status == 403:
return "AI provider forbidden the request"
case status == 429 || codeLower == "rate_limit_exceeded":
// OpenAI returns HTTP 429 for BOTH true rate limits and missing prepaid
// credits (insufficient_quota) — check quota BEFORE treating 429 as RPM.
case codeLower == "insufficient_quota" || typLower == "insufficient_quota" ||
strings.Contains(lower, "insufficient_quota") ||
strings.Contains(lower, "exceeded your current quota") ||
strings.Contains(lower, "check your plan and billing"):
return "API billing/quota empty — add prepaid API credits in Platform billing (not ChatGPT Plus)"
case codeLower == "rate_limit_exceeded" ||
strings.Contains(lower, "rate limit reached") ||
strings.Contains(lower, "rate_limit_exceeded"):
return "AI provider rate limited — retry later"
case codeLower == "insufficient_quota" || strings.Contains(lower, "insufficient_quota") ||
strings.Contains(lower, "exceeded your current quota"):
return "OpenAI quota exceeded — check billing"
case status == 429:
// Bare 429 with no body fields — still often quota on new accounts.
return "API HTTP 429 — usually empty Platform billing/credits (not ChatGPT Plus); add prepaid credits then retry"
case strings.Contains(lower, "does not exist") || strings.Contains(lower, "do not have access") ||
codeLower == "model_not_found":
return "model not found or API key lacks access — check model name and project permissions"
@@ -108,3 +118,23 @@ func ProbeCompleteOptions() CompleteOptions {
ReasoningEffort: "none",
}
}
// openAIErrorIsRetryableRateLimit is true only for genuine RPM/TPM limits.
// HTTP 429 + insufficient_quota (empty prepaid billing) must not be retried.
func openAIErrorIsRetryableRateLimit(errObj *openAIErrorBody) bool {
if errObj == nil {
return false
}
code := strings.ToLower(errObj.codeString())
typ := strings.ToLower(strings.TrimSpace(errObj.Type))
msg := strings.ToLower(strings.TrimSpace(errObj.Message))
if code == "insufficient_quota" || typ == "insufficient_quota" ||
strings.Contains(msg, "insufficient_quota") ||
strings.Contains(msg, "exceeded your current quota") ||
strings.Contains(msg, "check your plan and billing") {
return false
}
return code == "rate_limit_exceeded" ||
strings.Contains(msg, "rate limit reached") ||
strings.Contains(msg, "rate_limit_exceeded")
}