fix(errors): recover retryable/status from wrapped ProxyError

IsRetryable and GetStatusCode used a direct type assertion, so a ProxyError
wrapped with %w was never detected (status fell back to 500, retryable to
false). Use errors.As, which dereferences the existing Unwrap chain and
matches the file's own Unwrap method.

Test: Test_IsRetryable_GetStatusCode_Wrapped (wrapped error preserves both
flags).
Note: these helpers currently have no production callers (test-only
surface), so this is a correctness hardening of the error API.
This commit is contained in:
2026-08-13 03:10:15 +01:00
parent 0e8a8fe33f
commit b828915530
2 changed files with 24 additions and 2 deletions
+5 -2
View File
@@ -2,6 +2,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"time" "time"
) )
@@ -121,7 +122,8 @@ func IsRetryable(err error) bool {
return false return false
} }
if proxyErr, ok := err.(*ProxyError); ok { var proxyErr *ProxyError
if errors.As(err, &proxyErr) {
return proxyErr.Retryable return proxyErr.Retryable
} }
@@ -134,7 +136,8 @@ func GetStatusCode(err error) int {
return 200 return 200
} }
if proxyErr, ok := err.(*ProxyError); ok { var proxyErr *ProxyError
if errors.As(err, &proxyErr) {
return proxyErr.StatusCode return proxyErr.StatusCode
} }
+19
View File
@@ -2,6 +2,7 @@ package main
import ( import (
"errors" "errors"
"fmt"
"net/http" "net/http"
"testing" "testing"
@@ -241,3 +242,21 @@ func TestProxyError_Retryable(t *testing.T) {
}) })
} }
} }
// Test_IsRetryable_GetStatusCode_Wrapped verifies that retryability and status
// are correctly recovered from a ProxyError that has been wrapped (e.g. via
// %w), using errors.As rather than a direct type assertion.
func Test_IsRetryable_GetStatusCode_Wrapped(t *testing.T) {
base := NewProxyError(ErrCodeBackendError, "boom", 503, true)
wrapped := fmt.Errorf("request failed: %w", base)
assert.True(t, IsRetryable(wrapped), "retryable flag must survive wrapping")
assert.Equal(t, 503, GetStatusCode(wrapped), "status code must survive wrapping")
nonRetryable := NewProxyError(ErrCodeInvalidRequest, "bad", 400, false)
wrapped2 := fmt.Errorf("wrapped: %w", nonRetryable)
assert.False(t, IsRetryable(wrapped2))
assert.Equal(t, 400, GetStatusCode(wrapped2))
assert.False(t, IsRetryable(nil))
assert.Equal(t, 200, GetStatusCode(nil))
}