Skip to content

fix: cap Retry-After sleep to RetryWaitMax in DefaultBackoff#283

Open
nghiack7 wants to merge 1 commit into
hashicorp:mainfrom
nghiack7:fix/default-backoff-retry-after-max
Open

fix: cap Retry-After sleep to RetryWaitMax in DefaultBackoff#283
nghiack7 wants to merge 1 commit into
hashicorp:mainfrom
nghiack7:fix/default-backoff-retry-after-max

Conversation

@nghiack7
Copy link
Copy Markdown

@nghiack7 nghiack7 commented May 9, 2026

Problem

When a server responds with a Retry-After header (e.g. Retry-After: 3600), DefaultBackoff unconditionally sleeps for that duration — completely ignoring the client's configured RetryWaitMax. This means a misbehaving or malicious server can force a client to sleep for an arbitrarily long time, bypassing the user's intent.

Fixes #247.

Root cause

// before — no cap applied
if sleep, ok := parseRetryAfterHeader(resp.Header["Retry-After"]); ok {
    return sleep   // could be hours
}

Fix

Cap the Retry-After sleep at max (which is RetryWaitMax):

if sleep, ok := parseRetryAfterHeader(resp.Header["Retry-After"]); ok {
    if sleep > max {
        return max
    }
    return sleep
}

This is consistent with how the exponential backoff path already respects RetryWaitMax.

Tests

Added TestClient_DefaultBackoff_RetryAfterExceedsMax covering:

  • HTTP 429 with Retry-After: 3600 → capped to RetryWaitMax (10s)
  • HTTP 503 with Retry-After: 3600 → capped to RetryWaitMax (10s)

All existing tests pass.

@nghiack7 nghiack7 requested a review from a team as a code owner May 9, 2026 06:14
@hashicorp-cla-app
Copy link
Copy Markdown

CLA assistant check

Thank you for your submission! We require that all contributors sign our Contributor License Agreement ("CLA") before we can accept the contribution. Read and sign the agreement

Learn more about why HashiCorp requires a CLA and what the CLA includes


Nghĩa Nguyễn Ngọc seems not to be a GitHub user.
You need a GitHub account to be able to sign the CLA.
If you have already a GitHub account, please add the email address used for this commit to your account.

Have you signed the CLA already but the status is still pending? Recheck it.

DefaultBackoff respected the Retry-After header value without enforcing
the RetryWaitMax limit. A server could therefore force an arbitrarily
long wait (e.g. "Retry-After: 86400") even when the caller had set a
short RetryWaitMax.

Add a bounds check in DefaultBackoff so that the parsed Retry-After
duration is capped at max, consistent with how the exponential backoff
path already enforces the limit.

Add TestClient_DefaultBackoff_RetryAfterExceedsMax to cover the case
where the header value exceeds RetryWaitMax (429 and 503 variants).

Fixes hashicorp#247
@nghiack7 nghiack7 force-pushed the fix/default-backoff-retry-after-max branch from bf46940 to a5ab50a Compare May 11, 2026 02:15
@nghiack7
Copy link
Copy Markdown
Author

@hashicorp-cla-app recheck

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Default backoff doesn't honour RetryWaitMax when "Retry-After" header is sent in the response

1 participant