getsentry / getsentry/sentry-python

` RuntimeError`: deque mutated during iteration in `Scope.fork()` on Python 3.14 free-threaded

Đang mở
#5,465 3 bình luận 0 reaction 1 người được giao Được @sl0thentr0py nhận Xem trên GitHub
Bug Errors Python
Ngôn ngữ chính
Python
Star
2.2k
Fork
669
Merge trung bình
1 ngày 1 giờ
Pull request đã merge (30 ngày)
213

Mô tả

### How do you use Sentry?

Sentry Saas (sentry.io)

### Version

2.52.0

### Steps to Reproduce

Use `ThreadingIntegration(propagate_scope=True)` in the following example code :

```python
import sys
import threading

import sentry_sdk
from sentry_sdk.integrations.threading import ThreadingIntegration

def main():
print(f"Python {sys.version}")
print(f"sentry-sdk {sentry_sdk.VERSION}")
print(f"GIL enabled: {sys._is_gil_enabled()}")

sentry_sdk.init(
dsn="https://examplePublicKey@o0.ingest.sentry.io/0",
integrations=[ThreadingIntegration(propagate_scope=True)],
traces_sample_rate=0,
)

# Grab the main thread's isolation scope. The race requires multiple
# threads to operate on the same Scope object — one mutating
# _breadcrumbs, another copying it via fork().
shared_scope = sentry_sdk.get_isolation_scope()

stop = threading.Event()

def mutate_breadcrumbs():
"""Appends breadcrumbs to the shared scope's _breadcrumbs deque."""
i = 0
while not stop.is_set():
shared_scope.add_breadcrumb(
{"category": "test", "message": f"crumb-{i}", "level": "info"},
hint={},
)
i += 1

def fork_shared_scope():
"""Forks the shared scope, triggering copy.copy(deque)."""
while not stop.is_set():
shared_scope.fork()

print()

# Use a barrier so all threads start racing simultaneously.
num_workers = 4
barrier = threading.Barrier(num_workers + 1)

def run_with_barrier(fn):
barrier.wait()
fn()

threads: list[threading.Thread] = []
for _ in range(2):
t = threading.Thread(
target=run_with_barrier, args=(mutate_breadcrumbs,), daemon=True
)
t.start()
threads.append(t)
for _ in range(2):
t = threading.Thread(
target=run_with_barrier, args=(fork_shared_scope,), daemon=True
)
t.start()
threads.append(t)

# Release all threads at once
barrier.wait()

try:
stop.wait(timeout=5)
except KeyboardInterrupt:
pass
finally:
stop.set()
for t in threads:
t.join(timeout=5)

if __name__ == "__main__":
main()
```

### Expected Result

The program should finish without errors in Python 3.14 free-threaded mode.

### Actual Result

```
❯ python sentry_free_threaded_repro.py
Python 3.14.3 free-threading build (main, Feb 13 2026, 18:56:10) [GCC 11.4.0]
sentry-sdk 2.52.0
GIL enabled: False

Exception in thread Thread-3 (run_with_barrier):
Exception in thread Thread-4 (run_with_barrier):
Traceback (most recent call last):
Traceback (most recent call last):
File "lib/python3.14t/threading.py", line 1082, in _bootstrap_inner
self._context.run(self.run)
~~~~~~~~~~~~~~~~~^^^^^^^^^^
File "lib/python3.14t/site-packages/sentry_sdk/integrations/threading.py", line 140, in run
return _run_old_run_func()
File "lib/python3.14t/site-packages/sentry_sdk/integrations/threading.py", line 135, in _run_old_run_func
reraise(*_capture_exception())
~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "lib/python3.14t/site-packages/sentry_sdk/utils.py", line 1784, in reraise
raise value
File "lib/python3.14t/site-packages/sentry_sdk/integrations/threading.py", line 133, in _run_old_run_func
return old_run_func(self, *a[1:], **kw)
File "lib/python3.14t/threading.py", line 1024, in run
self._target(*self._args, **self._kwargs)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "sentry_free_threaded_repro.py", line 49, in run_with_barrier
fn()
~~^^
File "sentry_free_threaded_repro.py", line 39, in fork_shared_scope
shared_scope.fork()
~~~~~~~~~~~~~~~~~^^
File "lib/python3.14t/site-packages/sentry_sdk/scope.py", line 511, in fork
forked_scope = copy(self)
File "lib/python3.14t/copy.py", line 82, in copy
return copier(x)
File "lib/python3.14t/site-packages/sentry_sdk/scope.py", line 288, in __copy__
rv._breadcrumbs = copy(self._breadcrumbs)
~~~~^^^^^^^^^^^^^^^^^^^
File "lib/python3.14t/copy.py", line 82, in copy
return copier(x)
RuntimeError: deque mutated during iteration
File "lib/python3.14t/threading.py", line 1082, in _bootstrap_inner
self._context.run(self.run)
~~~~~~~~~~~~~~~~~^^^^^^^^^^
File "lib/python3.14t/site-packages/sentry_sdk/integrations/threading.py", line 140, in run
return _run_old_run_func()
File "lib/python3.14t/site-packages/sentry_sdk/integrations/threading.py", line 135, in _run_old_run_func
reraise(*_capture_exception())
~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "lib/python3.14t/site-packages/sentry_sdk/utils.py", line 1784, in reraise
raise value
File "lib/python3.14t/site-packages/sentry_sdk/integrations/threading.py", line 133, in _run_old_run_func
return old_run_func(self, *a[1:], **kw)
File "lib/python3.14t/threading.py", line 1024, in run
self._target(*self._args, **self._kwargs)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "sentry_free_threaded_repro.py", line 49, in run_with_barrier
fn()
~~^^
File "sentry_free_threaded_repro.py", line 39, in fork_shared_scope
shared_scope.fork()
~~~~~~~~~~~~~~~~~^^
File "lib/python3.14t/site-packages/sentry_sdk/scope.py", line 511, in fork
forked_scope = copy(self)
File "lib/python3.14t/copy.py", line 82, in copy
return copier(x)
File "lib/python3.14t/site-packages/sentry_sdk/scope.py", line 288, in __copy__
rv._breadcrumbs = copy(self._breadcrumbs)
~~~~^^^^^^^^^^^^^^^^^^^
File "lib/python3.14t/copy.py", line 82, in copy
return copier(x)
RuntimeError: deque mutated during iteration
```

As a workaround, we should be able to use a lock to protect calls to Scope's mutating methods. But it would be ideal if we could make the Scope class thread safe. What do you think?

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.