Skip to content

Commit 7a823c9

Browse files
authored
feat(transport): handle HTTP 413 responses for oversized envelopes (#982)
* feat(transport): handle HTTP 413 responses for oversized envelopes - Add :envelope_too_large reason to ClientError with specific error message - Handle HTTP 413 in Transport without retrying (similar to 429) - Record client report with :send_error reason per SDK spec - Log warning about envelope being rejected due to size limits This implements the SDK specification for handling HTTP 413 Content Too Large responses from Relay. Previously, oversized envelopes received HTTP 400 Bad Request, but Relay now returns HTTP 413 to allow SDKs to distinguish size-related rejections from other errors. Closes #978 Signed-off-by: Peter Solnica <peter@solnica.online> * docs: Update CHANGELOG.md --------- Signed-off-by: Peter Solnica <peter@solnica.online>
1 parent 9ab7fa9 commit 7a823c9

File tree

5 files changed

+79
-0
lines changed

5 files changed

+79
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Support for Structured Logs ([#969](https://github.com/getsentry/sentry-elixir/pull/969))
66
- Support for Distributed Tracing ([957](https://github.com/getsentry/sentry-elixir/pull/957))
77
- Support for LiveView spans captured under single trace root ([#977](https://github.com/getsentry/sentry-elixir/pull/977))
8+
- Handle HTTP 413 responses for oversized envelopes ([#982](https://github.com/getsentry/sentry-elixir/pull/982))
89

910
### Bug Fixes
1011

lib/sentry/client_error.ex

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ defmodule Sentry.ClientError do
3535
:too_many_retries
3636
| :rate_limited
3737
| :server_error
38+
| :envelope_too_large
3839
| {:invalid_json, Exception.t()}
3940
| {:request_failure, reason :: :inet.posix() | term()}
4041
| {Exception.kind(), reason :: term(), Exception.stacktrace()}
@@ -52,6 +53,16 @@ defmodule Sentry.ClientError do
5253
%__MODULE__{reason: :server_error, http_response: {status, headers, body}}
5354
end
5455

56+
@doc false
57+
@spec envelope_too_large(
58+
status :: 100..599,
59+
headers :: [{String.t(), String.t()}],
60+
body :: binary()
61+
) :: t
62+
def envelope_too_large(status, headers, body) do
63+
%__MODULE__{reason: :envelope_too_large, http_response: {status, headers, body}}
64+
end
65+
5566
@impl true
5667
def message(%__MODULE__{reason: reason, http_response: http_response}) do
5768
"Sentry failed to report event: #{format(reason, http_response)}"
@@ -66,6 +77,15 @@ defmodule Sentry.ClientError do
6677
"""
6778
end
6879

80+
defp format(:envelope_too_large, {status, headers, body}) do
81+
"""
82+
the envelope was rejected due to exceeding size limits.
83+
HTTP Status: #{status}
84+
Response Headers: #{inspect(headers)}
85+
Response Body: #{inspect(body)}
86+
"""
87+
end
88+
6989
defp format(reason, nil) do
7090
format(reason)
7191
end

lib/sentry/transport.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ defmodule Sentry.Transport do
5656
ClientReport.Sender.record_discarded_events(:ratelimit_backoff, envelope_items)
5757
{:error, ClientError.new(:rate_limited)}
5858

59+
{:error, {:envelope_too_large, {status, headers, body}}} ->
60+
ClientReport.Sender.record_discarded_events(:send_error, envelope_items)
61+
{:error, ClientError.envelope_too_large(status, headers, body)}
62+
5963
{:error, _reason} when retries_left != [] ->
6064
[sleep_interval | retries_left] = retries_left
6165
Process.sleep(sleep_interval)
@@ -99,6 +103,9 @@ defmodule Sentry.Transport do
99103
{:ok, 429, _headers, _body} ->
100104
{:error, :rate_limited}
101105

106+
{:ok, 413, headers, body} ->
107+
{:error, {:envelope_too_large, {413, headers, body}}}
108+
102109
{:ok, status, headers, body} ->
103110
{:error, {:http, {status, headers, body}}}
104111

test/sentry/client_error_test.exs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,22 @@ defmodule Sentry.ClientErrorTest do
4848
Response Body: "{}"
4949
"""
5050
end
51+
52+
test "with :envelope_too_large and HTTP response as the reason" do
53+
exception =
54+
ClientError.envelope_too_large(
55+
413,
56+
[{"X-Sentry-Error", "envelope too large"}],
57+
"Payload Too Large"
58+
)
59+
60+
assert Exception.message(exception) == """
61+
Sentry failed to report event: the envelope was rejected due to exceeding size limits.
62+
HTTP Status: 413
63+
Response Headers: [{"X-Sentry-Error", "envelope too large"}]
64+
Response Body: "Payload Too Large"
65+
"""
66+
end
5167
end
5268

5369
defp message_for_reason(reason) do

test/sentry/transport_test.exs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,41 @@ defmodule Sentry.TransportTest do
269269
assert_received {:request, ^ref}
270270
end
271271

272+
test "fails immediately when Sentry replies with 413 (envelope too large)", %{bypass: bypass} do
273+
envelope = Envelope.from_event(Event.create_event(message: "Hello"))
274+
test_pid = self()
275+
ref = make_ref()
276+
277+
Bypass.expect(bypass, "POST", "/api/1/envelope/", fn conn ->
278+
send(test_pid, {:request, ref})
279+
280+
conn
281+
|> Plug.Conn.put_resp_header("x-sentry-error", "Payload Too Large")
282+
|> Plug.Conn.resp(413, ~s<Payload Too Large>)
283+
end)
284+
285+
{:error, %ClientError{} = error} =
286+
Transport.encode_and_post_envelope(envelope, FinchClient, _retries = [100, 200])
287+
288+
assert error.reason == :envelope_too_large
289+
assert {413, headers, "Payload Too Large"} = error.http_response
290+
assert :proplists.get_value("x-sentry-error", headers, nil) == "Payload Too Large"
291+
292+
assert Exception.message(error) =~
293+
"the envelope was rejected due to exceeding size limits"
294+
295+
assert_received {:request, ^ref}
296+
refute_received {:request, ^ref}
297+
298+
log =
299+
capture_log(fn ->
300+
Transport.encode_and_post_envelope(envelope, FinchClient, _retries = [])
301+
end)
302+
303+
assert log =~ "[warning]"
304+
assert log =~ "exceeding size limits"
305+
end
306+
272307
test "updates rate limits from X-Sentry-Rate-Limits header in 200 OK response", %{
273308
bypass: bypass
274309
} do

0 commit comments

Comments
 (0)