Skip to content

fix: retry HTTP requests on transient 5xx responses - #763

Merged
trygve-lie merged 5 commits into
mainfrom
fix/w4-retry-on-transient-errors
Aug 28, 2026
Merged

fix: retry HTTP requests on transient 5xx responses#763
trygve-lie merged 5 commits into
mainfrom
fix/w4-retry-on-transient-errors

Conversation

@trygve-lie

@trygve-lie trygve-lie commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Adds retry logic with exponential backoff to the HTTP utilities used for package integrity checks and asset uploads. A transient server error (503, 502, 504) or network failure currently causes the operation to fail immediately with no recovery.

What changes

New utility: utils/http/retry.jsfetchWithRetry(fn, { maxRetries, baseDelayMs }) retries on 5xx and network-level errors. 4xx responses return immediately without retry.

Wired into:

  • utils/http/integrity.js — integrity hash lookups (used by Version.run() and publish)
  • utils/http/request.js — asset upload requests

Fixed: classes/integrity.js had a bare fetch() call that bypassed the retry logic — now uses fetchWithRetry.

Configurable retry count and delay

Retry behaviour is configurable at every layer:

CLI--retries flag on publish, version, and integrity commands:

eik publish --retries 0          # disable retries (fail fast)
eik publish --retries 5          # more aggressive retry
eik version --retries 0

APIretries and retryDelay options on all relevant entry points:

// disable retries for a single call
await cli.publish({ server, name, token, files, retries: 0 });

// custom retry count and base delay
await cli.version({ server, name, version, files, retries: 5, retryDelay: 200 });
await cli.integrity({ server, name, version, type: "package", retries: 2 });

Defaults: 3 retries, 500ms base delay (doubles each attempt: 500ms → 1000ms → 1500ms).

Tests

  • test/retry.test.js — 7 unit tests for fetchWithRetry utility
  • test/http-retry.test.js — 4 integration tests:
    • integrity.js: real Eik server with beforeEach/afterEach, mocked first call returns 503, retry reaches real server
    • request.js: mock-based (upload format complexity makes real-server testing add no value for this specific check)

A single transient server error (503, 502, 504) from the Eik server
causes the CLI to fail immediately with no recovery. Adds a shared
fetchWithRetry utility that retries up to three times with exponential
backoff on 5xx responses and network-level errors. 4xx responses are
not retried — they indicate client errors that will not resolve on
their own.

Wires the retry behaviour into:
- utils/http/integrity.js — used by Version.run() to fetch the current
  integrity hash before comparing against the local build
- utils/http/request.js — used by publish operations to upload assets
  to the Eik server
… the repo

integrity.js retry tests now use a real Eik server (fastify + memory
sink) with beforeEach/afterEach lifecycle matching other test files.
The first fetch call returns a mocked 503, the retry goes to the real
server and returns an actual integrity hash — verifying the retry
mechanism against a realistic response.

request.js retry tests remain mock-based: the upload format (tar
archive, multipart) makes real-server testing complex without adding
value for verifying the retry behaviour itself.
Adds retries and retryDelay options to all HTTP-related API entry
points (cli.publish, cli.version, cli.integrity) and a --retries
flag to the publish, version, and integrity commands.

Defaults remain 3 retries with 500ms base delay. Set retries: 0
to disable retry entirely.

Also wires fetchWithRetry into classes/integrity.js which previously
made its own bare fetch call without any retry protection.

@wkillerud wkillerud left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense 👍 Would be nice to have the default retry value listed in the JS docs and --help output.

Comment thread commands/version.js Outdated
},
retries: {
describe:
"Number of times to retry a request on transient server errors (0 to disable)",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to spell out the default value here, and maybe in some of the JSDoc as well. Had to dig quite a bit in the code to find the default now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

latest-version.js was the only HTTP utility in the fetch path that
lacked retry on transient 5xx responses. Wires in fetchWithRetry
alongside integrity.js and request.js. Also accepts retries and
retryDelay options for consistency with the other utilities.
- CLI --retries flag now shows default: 2 and explains the backoff schedule
- JSDoc @Property descriptions include the default value and what it means
- fetchWithRetry JSDoc clarifies that maxRetries is total attempts, not retries
- Fix retries→maxRetries mapping so user-facing retries=N means N additional
  retry attempts (retries=0 → 1 attempt; retries=2 → 3 total = default)
@digitalsadhu

Copy link
Copy Markdown
Member

An issue we had earlier is that 404s were being reported I think because thats what the bucket was reporting but retries fixed it because it was a blip in the machinery not an actual 404. These retries wont help against that. But maybe you've addressed the 404 issue??

@trygve-lie

Copy link
Copy Markdown
Contributor Author

There is done multiple improvements in the server to try to catch errors trough the whole upload, extraction and write process and report accurate http status codes when error happen. Hopefully real errors happening will now surface and not be masked behind a 404. Then this retry should be able to retry on errors and not found.

@trygve-lie
trygve-lie merged commit 4f66cad into main Aug 28, 2026
6 checks passed
@trygve-lie
trygve-lie deleted the fix/w4-retry-on-transient-errors branch August 28, 2026 11:32
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 3.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants