Skip to content

fix: Rate Limiting All Categories on Lowercased Header#8324

Merged
philipphofmann merged 7 commits into
mainfrom
fix/rate-limit-drops-all-categories
Jul 3, 2026
Merged

fix: Rate Limiting All Categories on Lowercased Header#8324
philipphofmann merged 7 commits into
mainfrom
fix/rate-limit-drops-all-categories

Conversation

@philipphofmann

@philipphofmann philipphofmann commented Jul 2, 2026

Copy link
Copy Markdown
Member

📜 Description

The SDK rate-limited all categories when receiving a lowercase HTTP X-Sentry-Rate-Limits header, because it didn't retrieve the rate limits header and fell back to rate limiting all categories when receiving a 429. So the SDK was over-ratelimiting. Now, the SDK retrieves the X-Sentry-Rate-Limits and Retry-After case-insensitively.

Since HTTP/2 headers must be lowercased

HTTP/2 — RFC 9113, Section 8.2.1 (Field Validity):
https://www.rfc-editor.org/rfc/rfc9113#section-8.2.1

A field name MUST NOT contain characters in the ranges 0x00-0x20, 0x41-0x5a, or 0x7f-0xff (all ranges inclusive). This specifically excludes all non-visible ASCII characters, ASCII SP (0x20), and uppercase characters ('A' to 'Z', ASCII 0x41 to 0x5a).

(i.e. uppercase letters are forbidden in field names → they are lowercase.)

HTTP/3 — RFC 9114, Section 4.2 (HTTP Fields):
https://www.rfc-editor.org/rfc/rfc9114#section-4.2

Characters in field names MUST be converted to lowercase prior to their encoding. A request or response containing uppercase characters in field names MUST be treated as malformed.

💡 Motivation and Context

Fixes #8322.

💚 How did you test it?

Unit tests.

📝 Checklist

You have to check all boxes before merging:

  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec.
  • If I added a new public API, I also added it to the SentryObjC wrapper.

philipphofmann and others added 3 commits July 2, 2026 17:57
Over HTTP/2 header field names arrive lowercased and recent Apple OS
versions no longer canonicalize custom header names. The case-sensitive
allHeaderFields lookup for X-Sentry-Rate-Limits then missed, so a
category-scoped 429 was treated as a rate limit for all categories.

Read X-Sentry-Rate-Limits and Retry-After case-insensitively via
value(forHTTPHeaderField:), with a case-sensitive fallback for the
macOS 10.14 deployment target.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@philipphofmann philipphofmann changed the title fix: dropping all events when a single category is rate limited fix: Rate Limiting All Categories on Lowercased Header Jul 3, 2026
@philipphofmann philipphofmann added the ready-to-merge Use this label to trigger all PR workflows label Jul 3, 2026
@sentry

sentry Bot commented Jul 3, 2026

Copy link
Copy Markdown

📲 Install Builds

iOS

🔗 App Name App ID Version Configuration
SDK-Size io.sentry.sample.SDK-Size 9.19.1 (1) Release

⚙️ sentry-cocoa Build Distribution Settings

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1225.02 ms 1250.72 ms 25.70 ms
Size 24.14 KiB 1.22 MiB 1.20 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
1155f0c 1222.41 ms 1251.18 ms 28.76 ms
dff20e3 1225.19 ms 1258.66 ms 33.47 ms
05bac3b 1220.16 ms 1255.81 ms 35.65 ms
79001eb 1226.09 ms 1258.79 ms 32.70 ms
66415de 1221.21 ms 1253.28 ms 32.07 ms
2fe9650 1228.33 ms 1260.78 ms 32.46 ms
b6fa517 1218.83 ms 1257.47 ms 38.63 ms
089f6f4 1219.37 ms 1253.95 ms 34.58 ms
89aaba2 1239.60 ms 1264.67 ms 25.08 ms
59213ba 1223.66 ms 1258.02 ms 34.36 ms

App size

Revision Plain With Sentry Diff
1155f0c 24.14 KiB 1.16 MiB 1.13 MiB
dff20e3 24.14 KiB 1.15 MiB 1.13 MiB
05bac3b 24.14 KiB 1.18 MiB 1.15 MiB
79001eb 24.14 KiB 1.19 MiB 1.16 MiB
66415de 24.14 KiB 1.16 MiB 1.13 MiB
2fe9650 24.14 KiB 1.17 MiB 1.15 MiB
b6fa517 24.14 KiB 1.14 MiB 1.12 MiB
089f6f4 24.14 KiB 1.18 MiB 1.15 MiB
89aaba2 24.14 KiB 1.16 MiB 1.14 MiB
59213ba 24.14 KiB 1.22 MiB 1.20 MiB

Previous results on branch: fix/rate-limit-drops-all-categories

Startup times

Revision Plain With Sentry Diff
1d1ff54 1239.60 ms 1268.96 ms 29.35 ms

App size

Revision Plain With Sentry Diff
1d1ff54 24.14 KiB 1.22 MiB 1.20 MiB

Comment thread CHANGELOG.md Outdated
@philipphofmann
philipphofmann marked this pull request as ready for review July 3, 2026 07:48

@itaybre itaybre 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.

LGTM, please update the changelog when you have the information

@philprime philprime left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM with one comment for you to consider

Comment thread Sources/Swift/Networking/DefaultRateLimits.swift Outdated
Comment thread Sources/Swift/Networking/DefaultRateLimits.swift
philipphofmann and others added 2 commits July 3, 2026 10:50
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@philipphofmann
philipphofmann merged commit 806a5e4 into main Jul 3, 2026
296 of 300 checks passed
@philipphofmann
philipphofmann deleted the fix/rate-limit-drops-all-categories branch July 3, 2026 11:48
philipphofmann added a commit that referenced this pull request Jul 6, 2026
The SDK rate-limited all categories when receiving a lowercase HTTP
X-Sentry-Rate-Limits header, because it looked the header up
case-sensitively via allHeaderFields[...], missed it, and fell back to
rate limiting all categories when receiving a 429. So the SDK was
over-ratelimiting. Now, the SDK retrieves the X-Sentry-Rate-Limits and
Retry-After case-insensitively.

Backport of #8324 to the v8 branch. The rate-limit code is still
Objective-C on v8.x (it was ported to Swift on main), so the fix is
reimplemented in SentryDefaultRateLimits.m. It uses
-[NSHTTPURLResponse valueForHTTPHeaderField:], which reads headers
case-insensitively, on iOS 13 / macOS 10.15 / tvOS 13 / watchOS 6 and
above, and falls back to a case-insensitive scan of allHeaderFields on
the lower v8 deployment targets (iOS 11 / macOS 10.13) where that API
isn't available.

Fixes #8322
philipphofmann added a commit that referenced this pull request Jul 6, 2026
The SDK rate-limited all categories when receiving a lowercase HTTP
X-Sentry-Rate-Limits header, because it looked the header up
case-sensitively via allHeaderFields[...], missed it, and fell back to
rate limiting all categories when receiving a 429. So the SDK was
over-ratelimiting. Now, the SDK retrieves the X-Sentry-Rate-Limits and
Retry-After case-insensitively.

Backport of #8324 to the v8 branch. The rate-limit code is still
Objective-C on v8.x (it was ported to Swift on main), so the fix is
reimplemented in SentryDefaultRateLimits.m. It uses
-[NSHTTPURLResponse valueForHTTPHeaderField:], which reads headers
case-insensitively, on iOS 13 / macOS 10.15 / tvOS 13 / watchOS 6 and
above, and falls back to a case-insensitive scan of allHeaderFields on
the lower v8 deployment targets (iOS 11 / macOS 10.13) where that API
isn't available.

Fixes #8322
philipphofmann added a commit that referenced this pull request Jul 6, 2026
The SDK rate-limited all categories when receiving a lowercase HTTP
X-Sentry-Rate-Limits header, because it looked the header up
case-sensitively via allHeaderFields[...], missed it, and fell back to
rate limiting all categories when receiving a 429. So the SDK was
over-ratelimiting. Now, the SDK retrieves the X-Sentry-Rate-Limits and
Retry-After case-insensitively.

Backport of #8324 to the v8 branch. The rate-limit code is still
Objective-C on v8.x (it was ported to Swift on main), so the fix is
reimplemented in SentryDefaultRateLimits.m. It uses
-[NSHTTPURLResponse valueForHTTPHeaderField:], which reads headers
case-insensitively, on iOS 13 / macOS 10.15 / tvOS 13 / watchOS 6 and
above, and falls back to a case-insensitive scan of allHeaderFields on
the lower v8 deployment targets (iOS 11 / macOS 10.13) where that API
isn't available.

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

Labels

approved ready-to-merge Use this label to trigger all PR workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

All event categories not sent by the SDK due to one category being rate limited

3 participants