Repository navigation
fix(no-ticket): do not retry a POST after a 500, 502 or 504 response - #563
Draft
cloudsmith-iduffy wants to merge 1 commit into
Draft
cloudsmith-iduffy wants to merge 1 commit into
cloudsmith-iduffy wants to merge 1 commit into
Conversation
A gateway can send these codes after the API processed the request. A retry then sends the POST again and can create a second entry. The CLI still retries a 429 or 503 response. A file upload keeps its retries, because a second upload of the same file is safe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Upload read-error retries are re-enabled before the body is rewindable, allowing exhausted-body retries to hang.
1 open issue
What changed in this PR
This PR extends the shared retry policy to prevent duplicate API operations after ambiguous server errors, building on #556’s read-error protection.
Changes:
- Stops POST and PATCH retries after 500, 502, or 504 responses while preserving other retry behavior.
- Adds an explicit retry opt-in for repeatable requests and enables it for uploads.
- Documents the behavior and adds request-count regression tests.
| File | Description |
|---|---|
cloudsmith_cli/core/tests/test_rest.py |
Tests method-specific status retries. |
cloudsmith_cli/core/tests/test_files.py |
Tests upload gateway-error retries. |
cloudsmith_cli/core/session.py |
Adds status-retry protection and the repeatable-request opt-in. |
cloudsmith_cli/core/api/files.py |
Enables upload retries through the opt-in. |
cloudsmith_cli/cli/decorators.py |
Clarifies retry-code option restrictions. |
CHANGELOG.md |
Documents API and upload retry behavior. |
Review effort: Balanced
|
|
||
| session = create_requests_session() | ||
| # A second upload of the same file replaces the first one. | ||
| session = create_requests_session(repeat_non_idempotent=True) |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Description
The CLI retried a 500, 502 or 504 response for every HTTP method, POST and PATCH included. These codes do not prove that the API ignored the request. The API CDN sends a 504 after 60 seconds while the API keeps working, and a 502 or 500 can arrive after the work is done. The retry then sent the POST again and could create a second entitlement, token, package or copy.
To reproduce, I used a local server that reads each POST and replies 504, and ran
cloudsmith entitlements create acme/repo --name ci:Before (base branch):
The server got 7 POSTs, and the command took 62 seconds.
After:
The server got 1 POST, and the command took 0.2 seconds.
What changes:
RetryWithCallback.is_retryno longer retries a POST or PATCH after a 500, 502 or 504 response. It uses the sameNON_IDEMPOTENT_METHODSset as the read-error rule from fix(no-ticket): stop a stalled POST from waiting forever #556.Retry-After. These codes mean that the server did not handle the request.--error-retry-codes: the CLI ignores 500, 502 and 504 for POST and PATCH, even when you list them. A user who sets this option wants retries for transient errors, not a second entry. The help text now says this.upload_filesends a POST to the upload URL. A second upload of the same file replaces the first, so the upload opts back in with the newcreate_requests_session(repeat_non_idempotent=True). The upload also keeps its retries after a read error or a dropped connection, which fix(no-ticket): stop a stalled POST from waiting forever #556 had turned off for it.Type of Change
Additional Notes
This PR is stacked on #556.
Why it has the breaking change label: the CLI now retries less for POST and PATCH. A script that ran a create command against a flaky gateway and depended on the automatic retry now gets the 500/502/504 error at once. The error also changes: before, the CLI reported
status: 0with aRetryErrorafter it used up its retries. Now it reports the real status (for example504 - Gateway Timeout), so the exit code changes too. In the repro above, it went from 1 to 248.Upload retries need #544. On this base, the upload body cannot be rewound, so a retry sends an empty body under the original
Content-Length. Upload retries already do this on master, so this PR does not make it worse, and #544 fixes it.Other POSTs that are safe to repeat, such as the OIDC token exchange and the Azure DevOps OIDC token request, also lose their 500/502/504 retries. A transient 502 during the OIDC exchange in CI now fails at once. They can opt in with
repeat_non_idempotent=Truein a follow-up if we want that.Tests: POST + 504 is sent once, POST + 503 is retried, GET + 504 is retried, and an upload POST + 504 is retried.
🤖 Generated with Claude Code