From 4f736d81ad60788aaa63f8bfe2e292800a3cb7be Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Tue, 27 Jan 2026 09:59:56 +0100 Subject: [PATCH 1/3] feat(transport): Report 413 responses for oversized envelopes --- sentry_sdk/transport.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/transport.py b/sentry_sdk/transport.py index cee4fa882b..dcfe55406b 100644 --- a/sentry_sdk/transport.py +++ b/sentry_sdk/transport.py @@ -340,7 +340,21 @@ def record_loss(reason: str) -> None: try: self._update_rate_limits(response) - if response.status == 429: + if response.status == 413: + size_exceeded_message = ( + "HTTP 413: Event dropped due to exceeded envelope size limit" + ) + response_message = getattr( + response, "data", getattr(response, "content", None) + ) + if response_message is not None: + size_exceeded_message += f" (body: {response_message})" + + logger.error(size_exceeded_message) + self.on_dropped_event("status_413") + record_loss("send_error") + + elif response.status == 429: # if we hit a 429. Something was rate limited but we already # acted on this in `self._update_rate_limits`. Note that we # do not want to record event loss here as we will have recorded From 56c019cda704eb35b2159dce0f6490530d4c6fb4 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Tue, 27 Jan 2026 16:07:39 +0100 Subject: [PATCH 2/3] add test --- tests/test_transport.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/test_transport.py b/tests/test_transport.py index b5b88e0e2c..411bccc9b5 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -385,6 +385,33 @@ def test_parse_rate_limits(input, expected): assert dict(_parse_rate_limits(input, now=NOW)) == expected +def test_envelope_too_large_response(capturing_server, make_client): + client = make_client() + + capturing_server.respond_with(code=413) + client.capture_event({"type": "error"}) + client.capture_event({"type": "transaction"}) + client.flush() + + # Error, transaction, and client report payloads + assert len(capturing_server.captured) == 3 + report = parse_json(capturing_server.captured[2].envelope.items[0].get_bytes()) + + # Client reports for transaction and included span + assert len(report["discarded_events"]) == 3 + assert {"reason": "send_error", "category": "error", "quantity": 1} in report[ + "discarded_events" + ] + assert {"reason": "send_error", "category": "span", "quantity": 1} in report[ + "discarded_events" + ] + assert {"reason": "send_error", "category": "transaction", "quantity": 1} in report[ + "discarded_events" + ] + + capturing_server.clear_captured() + + def test_simple_rate_limits(capturing_server, make_client): client = make_client() capturing_server.respond_with(code=429, headers={"Retry-After": "4"}) From 030886d375ae1ffadbdbcfd9573a5ec6b07b560e Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Tue, 27 Jan 2026 16:08:25 +0100 Subject: [PATCH 3/3] docstring --- tests/test_transport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_transport.py b/tests/test_transport.py index 411bccc9b5..8601a4f138 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -397,7 +397,7 @@ def test_envelope_too_large_response(capturing_server, make_client): assert len(capturing_server.captured) == 3 report = parse_json(capturing_server.captured[2].envelope.items[0].get_bytes()) - # Client reports for transaction and included span + # Client reports for error, transaction and included span assert len(report["discarded_events"]) == 3 assert {"reason": "send_error", "category": "error", "quantity": 1} in report[ "discarded_events"