| 91 | |
| 92 | // check if error is a permanent billing/quota exhaustion (not retryable) |
| 93 | function isPermanentUsageError(error: MessageV2.APIError): boolean { |
| 94 | const message = error.data.message.toLowerCase() |
| 95 | if (message.includes("usage_limit_reached") || message.includes("usage_not_included")) return true |
| 96 | |
| 97 | if (error.data.responseBody) { |
| 98 | try { |
| 99 | const body = JSON.parse(error.data.responseBody) |
| 100 | const errorObj = Array.isArray(body) ? body[0]?.error : body?.error |
| 101 | const type = errorObj?.type ?? "" |
| 102 | if (type === "usage_limit_reached" || type === "usage_not_included") return true |
| 103 | } catch {} |
| 104 | } |
| 105 | |
| 106 | return false |
| 107 | } |
| 108 | |
| 109 | // check if error is a rate limit error (429 or RESOURCE_EXHAUSTED) |
| 110 | function isRateLimitError(error: MessageV2.APIError): boolean { |