(error: MessageV2.APIError)
| 108 | |
| 109 | // check if error is a rate limit error (429 or RESOURCE_EXHAUSTED) |
| 110 | function isRateLimitError(error: MessageV2.APIError): boolean { |
| 111 | if (isPermanentUsageError(error)) return false |
| 112 | |
| 113 | // check status code |
| 114 | if (error.data.statusCode === 429) return true |
| 115 | |
| 116 | // check message for common rate limit patterns |
| 117 | const message = error.data.message.toLowerCase() |
| 118 | if (message.includes("rate limit") || message.includes("too many requests")) return true |
| 119 | if (message.includes("resource_exhausted") || message.includes("quota")) return true |
| 120 | |
| 121 | // check response body for google-specific errors |
| 122 | if (error.data.responseBody) { |
| 123 | try { |
| 124 | const body = JSON.parse(error.data.responseBody) |
| 125 | const errorObj = Array.isArray(body) ? body[0]?.error : body?.error |
| 126 | if (errorObj?.status === "RESOURCE_EXHAUSTED") return true |
| 127 | if (errorObj?.code === 429) return true |
| 128 | if (errorObj?.details?.some((d: any) => d.reason === "RATE_LIMIT_EXCEEDED")) return true |
| 129 | } catch {} |
| 130 | } |
| 131 | |
| 132 | return false |
| 133 | } |
| 134 | |
| 135 | export function retryable(error: ReturnType<NamedError["toObject"]>) { |
| 136 | if (MessageV2.APIError.isInstance(error)) { |
no test coverage detected