Skip to content

fix: drop attachments whose stream turns out empty when the envelope is sent - #5787

Draft
bitsandfoxes wants to merge 3 commits into
mainfrom
fix/drop-empty-attachments
Draft

bitsandfoxes wants to merge 3 commits into
mainfrom
fix/drop-empty-attachments

Conversation

@bitsandfoxes

@bitsandfoxes bitsandfoxes commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

An attachment whose content is a non-seekable stream was sent with length: 0 when the stream turned out empty. Relay then stored an empty file on the event.

Envelope.AddEnvelopeItemFromAttachment skips empty attachments only when stream.TryGetLength() returns 0. A non-seekable stream reports null, so the item was kept, and its header got length: 0 once the payload was buffered at serialization. sentry-unity hits this on purpose: its screenshot content returns a non-seekable stream so Envelope.FromEvent doesn't block waiting for the end-of-frame capture. When the screenshot is discarded (SetBeforeSendScreenshot returns null), times out or fails, the event shows an empty screenshot.jpg (see getsentry/sentry-unity#2835).

The fix acts at serialization, the first point where the payload size is known. Envelope.Serialize/SerializeAsync skip an attachment item, header and trailing newline included, when it was built from an attachment stream of unknown length and its buffered payload is empty. A warning is logged in the same wording as the checks at envelope creation.

  • Items with an explicit length: 0 are left alone. The envelope spec's framing example "Envelope with 2 empty attachments", which EnvelopeTests mirrors, keeps serializing as before.
  • The public EnvelopeItem.Serialize/SerializeAsync keep their signatures and behaviour (they always write). They delegate to new internal overloads that return whether the item was written, so Envelope can skip the item's trailing newline too. No public API change.
  • With offline caching on, the item is dropped before it's written to disk. Items read back from the cache are never dropped. Deserialization strips length, so the check keys on the payload type, not the header, and a deliberately empty attachment survives the round trip as on main.

An envelope whose only item is dropped, e.g. an empty non-seekable attachment sent via Hub.CaptureAttachment, goes out as a header-only envelope. Relay parses that and returns 200 without storing anything (handle_managed_envelope returns early when envelope.is_empty()), so the only cost is the request.

No client report is recorded for the drop, matching the existing drops at creation.

Tests: AttachmentSerializationTests gets a non-seekable empty stream attached through Envelope.FromEvent, serialized sync and async and deserialized again. Both tests failed before the change. A third test round-trips a known-length empty attachment through serialize, deserialize and serialize, and checks that it's kept.

🤖 Generated with Claude Code

bitsandfoxes and others added 3 commits October 23, 2026 22:09
Non-seekable attachment streams have no known length when the envelope
is created, so the existing zero-length check can't catch them. Skip the
item at serialization time, once its payload is buffered and before its
header is written.

Co-Authored-By: Claude <noreply@anthropic.com>
Keeps explicit length-0 items, as in the envelope spec's framing examples,
serializing as before.

Co-Authored-By: Claude <noreply@anthropic.com>
Deserialization strips the length header, so keying the drop on an unknown
length also dropped deliberately empty attachments after a cache round trip.
Key it on the payload built from a stream of unknown length instead.

Co-Authored-By: Claude <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.11765% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 76.16%. Comparing base (ff0355e) to head (3c27ce8).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/Sentry/Protocol/Envelopes/EnvelopeItem.cs 92.30% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5787      +/-   ##
==========================================
+ Coverage   76.13%   76.16%   +0.02%     
==========================================
  Files         528      528              
  Lines       19579    19590      +11     
  Branches     3829     3835       +6     
==========================================
+ Hits        14906    14920      +14     
+ Misses       3791     3789       -2     
+ Partials      882      881       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant