This commit is contained in:
OpenClaw-User 2026-03-14 17:08:36 +08:00
commit 8aeb9c8735
2 changed files with 50 additions and 0 deletions

View file

@ -50,6 +50,21 @@ var (
substr("context deadline exceeded"), substr("context deadline exceeded"),
} }
// Transport layer error patterns that should trigger fallback.
transportPatterns = []errorPattern{
substr("connection reset by peer"),
substr("connection refused"),
substr("no route to host"),
substr("unexpected eof"),
substr("broken pipe"),
substr("connection closed"),
substr("connection reset"),
substr("eof"),
substr("network is unreachable"),
substr("temporary failure in name resolution"),
substr("dial tcp"),
}
billingPatterns = []errorPattern{ billingPatterns = []errorPattern{
rxp(`\b402\b`), rxp(`\b402\b`),
substr("payment required"), substr("payment required"),
@ -195,6 +210,9 @@ func classifyByMessage(msg string) FailoverReason {
if matchesAny(msg, timeoutPatterns) { if matchesAny(msg, timeoutPatterns) {
return FailoverTimeout return FailoverTimeout
} }
if matchesAny(msg, transportPatterns) {
return FailoverTimeout // Transport errors treated as timeout (retriable)
}
if matchesAny(msg, authPatterns) { if matchesAny(msg, authPatterns) {
return FailoverAuth return FailoverAuth
} }

View file

@ -336,3 +336,35 @@ func TestIsImageSizeError(t *testing.T) {
t.Error("should not match normal error") t.Error("should not match normal error")
} }
} }
func TestClassifyError_TransportPatterns(t *testing.T) {
patterns := []string{
"connection reset by peer",
"connection refused",
"no route to host",
"unexpected EOF",
"broken pipe",
"connection closed",
"connection reset",
"EOF",
"network is unreachable",
"temporary failure in name resolution",
"dial tcp: lookup api.openrouter.ai: no such host",
"read tcp 10.0.0.1:12345->10.0.0.2:443: connection reset by peer",
}
for _, msg := range patterns {
err := errors.New(msg)
result := ClassifyError(err, "openrouter", "claude-3-opus")
if result == nil {
t.Errorf("pattern %q: expected non-nil", msg)
continue
}
if result.Reason != FailoverTimeout {
t.Errorf("pattern %q: reason = %q, want timeout", msg, result.Reason)
}
if !result.IsRetriable() {
t.Errorf("pattern %q: should be retriable", msg)
}
}
}