Skip to content

Handle Notehub rate limits with bounded automatic retries - #82

Open
rayozzie wants to merge 1 commit into
masterfrom
codex/notehub-rate-limit-retries
Open

rayozzie wants to merge 1 commit into
masterfrom
codex/notehub-rate-limit-retries

Conversation

@rayozzie

@rayozzie rayozzie commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

A brief burst of API requests can exhaust a billing account's allowance and make an otherwise valid Notehub command fail with HTTP 429. The hub can return that refusal without a body, leaving legacy commands with a JSON parsing error and little explanation.

This adds automatic 429 retries to the shared authenticated request path used by notehub api and the legacy V0/V1 commands. Commands wait for capacity and return the eventual response; persistent throttling produces an error that explains the account's limit when the hub supplies that information.

The retry delay follows server guidance in this order:

  1. A positive Retry-After value, expressed as seconds or an HTTP date.
  2. One request's share of the allowance described by X-RateLimit-Limit and X-RateLimit-Period, or X-RateLimit-Reset when that pair is unavailable, with jitter.
  3. Exponential backoff with jitter when no usable timing headers are present.

Each request allows at most six retries within a one-minute budget that includes request time and waits. If the next delay will not fit, the final 429 is returned intact. A server-requested delay is never shortened to fit the budget. This bounds retry scheduling without interrupting an in-flight request or a successful streaming response.

Retries require a replayable request body, continue to use the existing OAuth refresh handling, and respect context cancellation while waiting. For notehub api, an error message in the final response body takes precedence over the explanation derived from headers. --verbose reports each retry wait on stderr whether the flag appears before or after the API path, and a trailing --verbose=false suppresses those messages. The README documents the policy.

Validation passed in both the root module and lib/:

  • GOWORK=off go test -race -count=1 ./...
  • GOWORK=off go vet ./...

Tests cover header precedence, body replay, credential renewal, cancellation, exhausted retries, and final-response preservation. Clock-controlled tests verify the total budget without real minute-long waits; subprocess tests exercise CLI startup and flag placement.

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.

1 participant