Repository: getsentry/sentry-python

Issue:
Internal error if attachment files are missing
### How do you use Sentry?

Sentry Saas (sentry.io)

### Version

2.11.0

### Steps to Reproduce

Attach a file that doesn't exist:
```py
with sentry_sdk.configure_scope() as scope:
    scope.add_attachment(path='this_file_does_not_exist', filename='foo')
```
Then try to capture an event:
```py
division_by_zero = 1 / 0
```

### Expected Result

The event gets submitted successfully, without the missing file (potentially with some information that the file was not found, to help with debugging? but _anything_ showing up in the dashboard is better than nothing at all).

### Actual Result

The following errors get triggered, and no events show up in the Sentry dashboard:
```
[sentry] ERROR: Internal error in sentry_sdk
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/sentry_sdk/envelope.py", line 188, in get_bytes
    with open(self.path, "rb") as f:
FileNotFoundError: [Errno 2] No such file or directory: 'this_file_does_not_exist'
[sentry] ERROR: Internal error in sentry_sdk
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/sentry_sdk/transport.py", line 582, in send_envelope_wrapper
    self._send_envelope(envelope)
  File "/usr/local/lib/python3.10/dist-packages/sentry_sdk/transport.py", line 455, in _send_envelope
    envelope.serialize_into(f)
  File "/usr/local/lib/python3.10/dist-packages/sentry_sdk/envelope.py", line 137, in serialize_into
    item.serialize_into(f)
  File "/usr/local/lib/python3.10/dist-packages/sentry_sdk/envelope.py", line 310, in serialize_into
    headers["length"] = len(bytes)
TypeError: object of type 'NoneType' has no len()
```

Discussion:
codecov[bot]: **Test Failures Detected**: Due to failing tests, we cannot provide coverage reports at this time.

### :x: Failed Test Results: 
Completed 729 tests with **`1 failed`**, 720 passed and 8 skipped.
<details><summary>View the full list of failed tests</summary>

## py3.7-falcon-v1
- **Class name:** pytest<br>**Test name:** internal<br><br>
  <pre>No failure message available</pre>
</details>

Patch:
diff --git a/sentry_sdk/envelope.py b/sentry_sdk/envelope.py
index 44cce524..6bb1eb22 100644
--- a/sentry_sdk/envelope.py
+++ b/sentry_sdk/envelope.py
@@ -189,9 +189,7 @@ class PayloadRef:
                         self.bytes = f.read()
             elif self.json is not None:
                 self.bytes = json_dumps(self.json)
-            else:
-                self.bytes = b""
-        return self.bytes
+        return self.bytes or b""
 
     @property
     def inferred_content_type(self):


Test patch:
diff --git a/tests/test_basics.py b/tests/test_basics.py
index 3a801c57..e1e84340 100644
--- a/tests/test_basics.py
+++ b/tests/test_basics.py
@@ -459,6 +459,22 @@ def test_attachments(sentry_init, capture_envelopes):
         assert pyfile.payload.get_bytes() == f.read()
 
 
+@pytest.mark.tests_internal_exceptions
+def test_attachments_graceful_failure(
+    sentry_init, capture_envelopes, internal_exceptions
+):
+    sentry_init()
+    envelopes = capture_envelopes()
+
+    with configure_scope() as scope:
+        scope.add_attachment(path="non_existent")
+    capture_exception(ValueError())
+
+    (envelope,) = envelopes
+    assert len(envelope.items) == 2
+    assert envelope.items[1].payload.get_bytes() == b""
+
+
 def test_integration_scoping(sentry_init, capture_events):
     logger = logging.getLogger("test_basics")