Skip to content

feat: surface 429s as typed BugSplatRateLimitError#168

Merged
bobbyg603 merged 4 commits into
mainfrom
feat/rate-limit-error
Jun 8, 2026
Merged

feat: surface 429s as typed BugSplatRateLimitError#168
bobbyg603 merged 4 commits into
mainfrom
feat/rate-limit-error

Conversation

@bobbyg603

@bobbyg603 bobbyg603 commented Jun 8, 2026

Copy link
Copy Markdown
Member

Why

Rate-limited (HTTP 429) responses are currently thrown as a generic Error('...too many requests'), leaving consumers (e.g. symbol-upload #184) to match on the message string to detect them — brittle and easy to break.

What

  • New BugSplatRateLimitError (src/common/client/api-client.ts) with an isRateLimitError marker and status, mirroring the existing BugSplatAuthenticationError pattern. Exported via the common/client barrel.
  • Throw the typed error at all three 429 sites: SymbolsApiClient.getPresignedUrl, VersionsApiClient.getPresignedUrl, and CrashPostClient.getCrashUploadUrl.

Non-breaking: new export + a more specific error subclass; the error messages and throw-based control flow are unchanged.

Not included

No Retry-After handling. AWS WAF doesn't emit Retry-After by default and its custom response headers are static (can't reflect the real remaining window), so consumers can't rely on it — symbol-upload backs off with a shared exponential gate instead. If we ever want an accurate hint, the right place to emit it is the application layer, and we can add it then.

Tests

  • New api-client.spec.ts covering the error shape (isRateLimitError, default status 429).
  • 429 cases added to the symbols and versions client specs asserting the typed error is thrown.
  • Full suite green: 239 specs, 0 failures.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings June 8, 2026 17:13

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR improves rate-limit handling by introducing a typed BugSplatRateLimitError (including parsed Retry-After seconds) and by plumbing HTTP response headers through BugSplatResponse so callers can make informed backoff decisions across both cookie-auth and OAuth client paths.

Changes:

  • Added BugSplatRateLimitError plus parseRetryAfterSeconds() to parse Retry-After and expose retryAfterSeconds to consumers.
  • Extended BugSplatResponse to optionally include headers, and updated both API client implementations to pass through response.headers.
  • Updated symbols/versions/crash-post clients to throw the typed error on HTTP 429, with new/expanded specs (symbols + versions + common helper).

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/versions/versions-api-client/versions-api-client.ts Throw BugSplatRateLimitError on 429 and parse Retry-After.
src/versions/versions-api-client/versions-api-client.spec.ts Add 429 test asserting typed error + retryAfterSeconds.
src/symbols/symbols-api-client/symbols-api-client.ts Throw BugSplatRateLimitError on 429 and parse Retry-After.
src/symbols/symbols-api-client/symbols-api-client.spec.ts Add 429 test asserting typed error + retryAfterSeconds.
src/post/crash-post-client.ts Throw BugSplatRateLimitError on 429 and parse Retry-After.
src/common/client/oauth-client-credentials-api-client/oauth-client-credentials-api-client.ts Pass response.headers through in returned BugSplatResponse.
src/common/client/index.ts Re-export new error/type/helper from the client barrel.
src/common/client/bugsplat-api-client/bugsplat-api-client.ts Pass response.headers through in returned BugSplatResponse.
src/common/client/api-client.ts Add headers to BugSplatResponse, add BugSplatRateLimitError, add parseRetryAfterSeconds.
src/common/client/api-client.spec.ts Add unit tests for the new error type and parseRetryAfterSeconds.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/common/client/api-client.ts Outdated
Comment thread src/post/crash-post-client.ts
Comment thread src/common/client/bugsplat-api-client/bugsplat-api-client.ts
Rate-limited (HTTP 429) responses were thrown as a generic Error with a
"too many requests" message, leaving consumers to match on the message
string to detect them.

Add a BugSplatRateLimitError (isRateLimitError marker + status) and throw
it from the three presigned-URL sites (symbols/versions getPresignedUrl,
crash-post getCrashUploadUrl) so consumers can detect rate limiting by
type. Exported via the common/client barrel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bobbyg603 bobbyg603 changed the title feat: surface 429s as typed BugSplatRateLimitError with retry-after feat: surface 429s as typed BugSplatRateLimitError Jun 8, 2026
@bobbyg603 bobbyg603 force-pushed the feat/rate-limit-error branch from 10398f7 to 089d685 Compare June 8, 2026 17:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Comment thread src/common/client/api-client.ts
bobbyg603 and others added 3 commits June 8, 2026 16:28
Addresses Copilot review feedback on #168:
- Remove trailing comma after the BugSplatRateLimitError `status`
  constructor param (incompatible with Prettier `trailingComma: es5`).
- Add a spec asserting CrashPostClient throws BugSplatRateLimitError
  with status 429 when getCrashUploadUrl returns 429.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- actions/checkout@main -> @v6, actions/setup-node@v4 -> @v6 (both now
  run on the Node 24 runtime, clearing the node20 deprecation warning).
- node-version 22.x -> 24.x.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Husky's hook bootstrap is deprecated (fails in v10), and its commit-msg
hook only linted individual branch commits that get squashed away on
merge. Since PRs squash-merge with the PR title as the commit subject,
that title is what semantic-release parses for version bumps.

- Remove husky (dep, `prepare` script, .husky/, core.hooksPath).
- Remove @commitlint/cli, @commitlint/config-conventional, .commitlintrc.js
  (only ever invoked by the husky commit-msg hook).
- Add a PR Title workflow using amannn/action-semantic-pull-request to
  validate the PR title against Conventional Commits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bobbyg603 bobbyg603 merged commit d7afcb1 into main Jun 8, 2026
5 checks passed
@bobbyg603 bobbyg603 deleted the feat/rate-limit-error branch June 8, 2026 21:10
bobbyg603 pushed a commit that referenced this pull request Jun 8, 2026
# [15.2.0](v15.1.0...v15.2.0) (2026-06-08)

### Features

* surface 429s as typed BugSplatRateLimitError ([#168](#168)) ([d7afcb1](d7afcb1))
@bobbyg603

Copy link
Copy Markdown
Member Author

🎉 This PR is included in version 15.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