Skip to content

Commit ba35a71

Browse files
committed
fix(backend): short-circuit retry.Do on 401/403 auth errors from the registry
pull and push wrap per-layer processing in retry.Do with defaultRetryOpts, which retries up to 6 times on exponential 5s..60s backoff. The options didn't set a RetryIf predicate, so every error was treated as transient - including 401 Unauthorized and 403 Forbidden from the registry. Auth errors never recover on retry with the same credentials, so the user sat through ~30-60s of silent backoff before seeing the real failure. Add a predicate that unwraps the error chain looking for oras.land/oras-go/v2/registry/remote/errcode.ErrorResponse (the typed error oras emits for HTTP responses the registry rejected) and returns false for 401/403. Transient HTTP failures (5xx, network resets, registry rate-limit 429, etc.) still retry as before. Fixes #494 Signed-off-by: SAY-5 <SAY-5@users.noreply.github.com> Signed-off-by: Sai Asish Y <say.apm35@gmail.com>
1 parent f5cd9a6 commit ba35a71

1 file changed

Lines changed: 19 additions & 0 deletions

File tree

pkg/backend/retry.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,33 @@
1717
package backend
1818

1919
import (
20+
"errors"
21+
"net/http"
2022
"time"
2123

2224
retry "github.com/avast/retry-go/v4"
25+
"oras.land/oras-go/v2/registry/remote/errcode"
2326
)
2427

28+
// isAuthError reports whether err represents a registry auth failure that
29+
// cannot be fixed by retrying the same request with the same credentials.
30+
// Retrying those wastes the user's time on the ~30s+ exponential backoff
31+
// before the final error surfaces, so callers should short-circuit instead.
32+
func isAuthError(err error) bool {
33+
var respErr *errcode.ErrorResponse
34+
if errors.As(err, &respErr) {
35+
return respErr.StatusCode == http.StatusUnauthorized ||
36+
respErr.StatusCode == http.StatusForbidden
37+
}
38+
return false
39+
}
40+
2541
var defaultRetryOpts = []retry.Option{
2642
retry.Attempts(6),
2743
retry.DelayType(retry.BackOffDelay),
2844
retry.Delay(5 * time.Second),
2945
retry.MaxDelay(60 * time.Second),
46+
// Registry auth errors will not recover on retry; fail fast so the user
47+
// sees the real error within seconds instead of 30s+ of silent backoff.
48+
retry.RetryIf(func(err error) bool { return !isAuthError(err) }),
3049
}

0 commit comments

Comments
 (0)