From ca1bde5e18481de48a4220d288dc57506379f83b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=96=87=E9=94=8B0668000834?= Date: Fri, 13 Mar 2026 09:15:55 +0800 Subject: [PATCH] fix(providers): classify transport errors as retriable for fallback chain Add transport-level connection error patterns to error classifier: - connection reset by peer - connection refused - no route to host - unexpected eof - broken pipe - network is unreachable - connection closed - tls handshake errors - read/write tcp errors These errors are now classified as FailoverTimeout (retriable), allowing the fallback chain to continue instead of aborting on unclassified errors. Fixes #1419 (part 1) --- pkg/providers/error_classifier.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pkg/providers/error_classifier.go b/pkg/providers/error_classifier.go index fd9bf1e81..35af3b103 100644 --- a/pkg/providers/error_classifier.go +++ b/pkg/providers/error_classifier.go @@ -50,6 +50,20 @@ var ( substr("context deadline exceeded"), } + // Transport-level connection errors that should trigger fallback + transportErrorPatterns = []errorPattern{ + substr("connection reset by peer"), + substr("connection refused"), + substr("no route to host"), + substr("unexpected eof"), + substr("broken pipe"), + substr("network is unreachable"), + substr("connection closed"), + substr("tls handshake"), + substr("read tcp"), + substr("write tcp"), + } + billingPatterns = []errorPattern{ rxp(`\b402\b`), substr("payment required"), @@ -195,6 +209,9 @@ func classifyByMessage(msg string) FailoverReason { if matchesAny(msg, timeoutPatterns) { return FailoverTimeout } + if matchesAny(msg, transportErrorPatterns) { + return FailoverTimeout // Transport errors treated as timeout (retriable) + } if matchesAny(msg, authPatterns) { return FailoverAuth }