@@ -213,6 +213,43 @@ func IsRetryableStatusCode(statusCode int) bool {
213213 }
214214}
215215
216+ // retryablePatterns contains error message substrings that indicate a
217+ // transient/retryable failure. Numeric status codes (500, 502, etc.) are
218+ // handled separately by ExtractHTTPStatusCode + IsRetryableStatusCode.
219+ var retryablePatterns = []string {
220+ "timeout" , // Generic timeout
221+ "connection reset" , // Connection reset
222+ "connection refused" , // Connection refused
223+ "no such host" , // DNS failure
224+ "temporary failure" , // Temporary failure
225+ "service unavailable" , // Service unavailable
226+ "internal server error" , // Server error
227+ "bad gateway" , // Gateway error
228+ "gateway timeout" , // Gateway timeout
229+ "overloaded" , // Server overloaded
230+ "overloaded_error" , // Server overloaded
231+ "other side closed" , // Connection closed by peer
232+ "fetch failed" , // Network fetch failure
233+ "reset before headers" , // Connection reset before headers received
234+ "upstream connect" , // Upstream connection error
235+ "internal_error" , // HTTP/2 INTERNAL_ERROR (stream-level)
236+ }
237+
238+ // nonRetryablePatterns contains error message substrings that indicate a
239+ // permanent/non-retryable failure. Numeric status codes (429, 401, etc.) are
240+ // handled separately by ExtractHTTPStatusCode.
241+ var nonRetryablePatterns = []string {
242+ "rate limit" , // Rate limit message
243+ "too many requests" , // Rate limit message
244+ "throttl" , // Throttling (rate limiting)
245+ "quota" , // Quota exceeded
246+ "capacity" , // Capacity issues (often rate-limit related)
247+ "invalid" , // Invalid request
248+ "unauthorized" , // Auth error
249+ "authentication" , // Auth error
250+ "api key" , // API key error
251+ }
252+
216253// isRetryableModelError determines if an error should trigger a retry of the SAME model.
217254// It is used as a fallback by ClassifyModelError when no *StatusError is present.
218255//
@@ -268,55 +305,15 @@ func isRetryableModelError(err error) bool {
268305 }
269306 }
270307
271- // Fall back to message-pattern matching for errors without structured status codes
272308 errMsg := strings .ToLower (err .Error ())
273309
274- // Retryable patterns (timeout, network issues)
275- // NOTE: Numeric status codes (500, 502, etc.) are already handled by
276- // ExtractHTTPStatusCode + IsRetryableStatusCode above; they are not
277- // duplicated here to avoid false positives on arbitrary numbers in
278- // error messages (e.g., "processed 500 items").
279- retryablePatterns := []string {
280- "timeout" , // Generic timeout
281- "connection reset" , // Connection reset
282- "connection refused" , // Connection refused
283- "no such host" , // DNS failure
284- "temporary failure" , // Temporary failure
285- "service unavailable" , // Service unavailable
286- "internal server error" , // Server error
287- "bad gateway" , // Gateway error
288- "gateway timeout" , // Gateway timeout
289- "overloaded" , // Server overloaded
290- "overloaded_error" , // Server overloaded
291- "other side closed" , // Connection closed by peer
292- "fetch failed" , // Network fetch failure
293- "reset before headers" , // Connection reset before headers received
294- "upstream connect" , // Upstream connection error
295- "internal_error" , // HTTP/2 INTERNAL_ERROR (stream-level)
296- }
297-
298310 for _ , pattern := range retryablePatterns {
299311 if strings .Contains (errMsg , pattern ) {
300312 slog .Debug ("Matched retryable error pattern" , "pattern" , pattern )
301313 return true
302314 }
303315 }
304316
305- // Non-retryable patterns (skip to next model immediately)
306- // NOTE: Numeric status codes (429, 401, etc.) are already handled by
307- // ExtractHTTPStatusCode above; they are not duplicated here.
308- nonRetryablePatterns := []string {
309- "rate limit" , // Rate limit message
310- "too many requests" , // Rate limit message
311- "throttl" , // Throttling (rate limiting)
312- "quota" , // Quota exceeded
313- "capacity" , // Capacity issues (often rate-limit related)
314- "invalid" , // Invalid request
315- "unauthorized" , // Auth error
316- "authentication" , // Auth error
317- "api key" , // API key error
318- }
319-
320317 for _ , pattern := range nonRetryablePatterns {
321318 if strings .Contains (errMsg , pattern ) {
322319 slog .Debug ("Matched non-retryable error pattern" , "pattern" , pattern )
0 commit comments