From 918a8297dc28e46b2f3add02e8e020021a53cac4 Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Thu, 5 Mar 2026 14:24:36 +0100 Subject: [PATCH] feat(transport): Handle HTTP 413 response for oversized envelopes When Relay returns HTTP 413 (Content Too Large), the SDK now logs a specific warning message and records client reports with reason "send_error" for the dropped items. The 413 is not retried since the data is definitively too large. Co-Authored-By: Claude Opus 4.6 --- sentry-ruby/lib/sentry/exceptions.rb | 3 + sentry-ruby/lib/sentry/transport.rb | 7 +- .../lib/sentry/transport/http_transport.rb | 6 ++ .../sentry/transport/http_transport_spec.rb | 73 +++++++++++++++++++ 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/sentry-ruby/lib/sentry/exceptions.rb b/sentry-ruby/lib/sentry/exceptions.rb index 515e7d660..0c8e43702 100644 --- a/sentry-ruby/lib/sentry/exceptions.rb +++ b/sentry-ruby/lib/sentry/exceptions.rb @@ -6,4 +6,7 @@ class Error < StandardError class ExternalError < Error end + + class SizeExceededError < ExternalError + end end diff --git a/sentry-ruby/lib/sentry/transport.rb b/sentry-ruby/lib/sentry/transport.rb index 503e6a2ac..d799dc7e2 100644 --- a/sentry-ruby/lib/sentry/transport.rb +++ b/sentry-ruby/lib/sentry/transport.rb @@ -19,7 +19,8 @@ class Transport :before_send, :event_processor, :insufficient_data, - :backpressure + :backpressure, + :send_error ] include LoggingHelper @@ -61,6 +62,10 @@ def send_envelope(envelope) log_debug("[Transport] Sending envelope with items [#{serialized_items.map(&:type).join(', ')}] #{envelope.event_id} to Sentry") send_data(data) end + rescue Sentry::SizeExceededError + serialized_items&.each do |item| + record_lost_event(:send_error, item.data_category) + end end def serialize_envelope(envelope) diff --git a/sentry-ruby/lib/sentry/transport/http_transport.rb b/sentry-ruby/lib/sentry/transport/http_transport.rb index 47cf6c4e7..88902557f 100644 --- a/sentry-ruby/lib/sentry/transport/http_transport.rb +++ b/sentry-ruby/lib/sentry/transport/http_transport.rb @@ -49,6 +49,12 @@ def send_data(data) if response.code.match?(/\A2\d{2}/) handle_rate_limited_response(response) if has_rate_limited_header?(response) + elsif response.code == "413" + error_message = "HTTP 413: Envelope dropped due to exceeded size limit" + error_message += " (body: #{response.body})" if response.body && !response.body.empty? + log_warn(error_message) + + raise Sentry::SizeExceededError, error_message elsif response.code == "429" log_debug("the server responded with status 429") handle_rate_limited_response(response) diff --git a/sentry-ruby/spec/sentry/transport/http_transport_spec.rb b/sentry-ruby/spec/sentry/transport/http_transport_spec.rb index 4070bddd1..bd722d28f 100644 --- a/sentry-ruby/spec/sentry/transport/http_transport_spec.rb +++ b/sentry-ruby/spec/sentry/transport/http_transport_spec.rb @@ -291,6 +291,79 @@ end describe "failed request handling" do + context "receive 413 response" do + let(:string_io) { StringIO.new } + + before do + configuration.sdk_logger = Logger.new(string_io) + end + + context "with response body" do + let(:fake_response) { build_fake_response("413", body: { detail: "Envelope too large" }) } + + it "raises SizeExceededError with body in message" do + sentry_stub_request(fake_response) + + expect { subject.send_data(data) }.to raise_error( + Sentry::SizeExceededError, + /HTTP 413: Envelope dropped due to exceeded size limit.*body:.*Envelope too large/ + ) + end + + it "logs a warning message with body" do + sentry_stub_request(fake_response) + + expect { subject.send_data(data) }.to raise_error(Sentry::SizeExceededError) + expect(string_io.string).to include("HTTP 413: Envelope dropped due to exceeded size limit") + expect(string_io.string).to include("Envelope too large") + end + end + + context "with empty response body" do + let(:fake_response) do + Net::HTTPResponse.new("1.0", "413", "").tap do |response| + allow(response).to receive(:body).and_return("") + end + end + + it "raises SizeExceededError without body in message" do + sentry_stub_request(fake_response) + + expect { subject.send_data(data) }.to raise_error( + Sentry::SizeExceededError, + "HTTP 413: Envelope dropped due to exceeded size limit" + ) + end + end + + context "records client reports via send_envelope" do + let(:fake_response) { build_fake_response("413", body: { detail: "too large" }) } + + it "records send_error for each item in the envelope" do + sentry_stub_request(fake_response) + + configuration.send_client_reports = true + transport = Sentry::HTTPTransport.new(configuration) + envelope = transport.envelope_from_event(event) + + transport.send_envelope(envelope) + + # Should have recorded send_error for the event item + expect(transport.discarded_events[[:send_error, "error"]]).to eq(1) + end + + it "does not raise the error to the caller" do + sentry_stub_request(fake_response) + + configuration.send_client_reports = true + transport = Sentry::HTTPTransport.new(configuration) + envelope = transport.envelope_from_event(event) + + expect { transport.send_envelope(envelope) }.not_to raise_error + end + end + end + context "receive 4xx responses" do let(:fake_response) { build_fake_response("404") }