Summary
retryer() is named "retryer", but it does token rotation, not transient retry. The retry count equals the number of PATs:
for (let retries = 0; retries < PATs.length; retries++) {
With a single PAT_1, the fetcher runs at most once. There is no retry for transient failures.
Details
Network-level errors bail out immediately:
// network/unexpected error → let caller treat as failure
if (!e.response) {
throw err;
}
So these common transient errors never get retried:
ECONNRESET
ETIMEDOUT
- socket hang up
- TLS connection reset
- DNS temporary failure
Why it matters
One stats card is a multi-request transaction: GraphQL stats, per-repo star lookups, REST search for total commits. A single transient failure anywhere fails the whole card. The next run usually succeeds, so users see random red builds and manually re-run.
Observed pattern in GitHub Actions:
request 1 ✅ ... request 18 ✅ request 19 ❌ transient → card fails
manual re-run → all requests ✅ → card succeeds
Proposal
Token rotation and transient retry are different concerns. Keep the rotation loop over PATs, and add an inner retry with exponential backoff plus jitter for the same PAT:
- Retry when the error has no response (network level) or the HTTP status is 429/502/503/504.
- Do not retry permanent failures such as 401 bad credentials, 404 not found, or 422 invalid query.
- Suggested schedule: 1s, 2s, 4s, each plus a small jitter.
- Keep the function signature unchanged; expose delays as an optional parameter for tests.
I have a working implementation with tests and will open a PR.
Summary
retryer()is named "retryer", but it does token rotation, not transient retry. The retry count equals the number of PATs:With a single
PAT_1, the fetcher runs at most once. There is no retry for transient failures.Details
Network-level errors bail out immediately:
So these common transient errors never get retried:
ECONNRESETETIMEDOUTWhy it matters
One stats card is a multi-request transaction: GraphQL stats, per-repo star lookups, REST search for total commits. A single transient failure anywhere fails the whole card. The next run usually succeeds, so users see random red builds and manually re-run.
Observed pattern in GitHub Actions:
Proposal
Token rotation and transient retry are different concerns. Keep the rotation loop over PATs, and add an inner retry with exponential backoff plus jitter for the same PAT:
I have a working implementation with tests and will open a PR.