With ddtrace==4.6.3 it is not reproducible in production.
... TRUNCATED STACKTRACE
File "/usr/src/app/cardapp/adapter/stripe/__init__.py", line 780, in get_payment_method
return stripe.PaymentMethod.retrieve(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
payment_method_id,
^^^^^^^^^^^^^^^^^^
...<2 lines>...
stripe_version=self.api_version,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/venv/lib/python3.14/site-packages/stripe/_payment_method.py", line 1993, in retrieve
instance.refresh()
~~~~~~~~~~~~~~~~^^
File "/venv/lib/python3.14/site-packages/stripe/_api_resource.py", line 38, in refresh
return self._request_and_refresh("get", self.instance_url())
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv/lib/python3.14/site-packages/stripe/_api_resource.py", line 128, in _request_and_refresh
obj = StripeObject._request(
self,
...<4 lines>...
usage=usage,
)
File "/venv/lib/python3.14/site-packages/stripe/_stripe_object.py", line 424, in _request
return self._requestor.request(
~~~~~~~~~~~~~~~~~~~~~~~^
method,
^^^^^^^
...<4 lines>...
usage=usage,
^^^^^^^^^^^^
)
^
File "/venv/lib/python3.14/site-packages/stripe/_api_requestor.py", line 201, in request
rbody, rcode, rheaders = requestor.request_raw(
~~~~~~~~~~~~~~~~~~~~~^
method.lower(),
^^^^^^^^^^^^^^^
...<6 lines>...
usage=usage,
^^^^^^^^^^^^
)
^
File "/venv/lib/python3.14/site-packages/stripe/_api_requestor.py", line 730, in request_raw
) = self._args_for_request_with_retries(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
method,
^^^^^^^
...<5 lines>...
usage=usage,
^^^^^^^^^^^^
)
^
File "/venv/lib/python3.14/site-packages/stripe/_api_requestor.py", line 653, in _args_for_request_with_retries
headers = self.request_headers(
# this cast is safe because the blocks below validate that `method` is one of the allowed values
...<2 lines>...
request_options,
)
File "/venv/lib/python3.14/site-packages/stripe/_api_requestor.py", line 521, in request_headers
val = func()
File "/usr/local/lib/python3.14/platform.py", line 1354, in platform
system, node, release, version, machine, processor = uname()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.14/platform.py", line 959, in __iter__
(self.processor,)
^^^^^^^^^^^^^^
File "/usr/local/lib/python3.14/functools.py", line 1126, in __get__
val = self.func(instance)
File "/usr/local/lib/python3.14/platform.py", line 954, in processor
return _unknown_as_blank(_Processor.get())
~~~~~~~~~~~~~~^^
File "/usr/local/lib/python3.14/platform.py", line 886, in get
return func() or ''
~~~~^^
File "/usr/local/lib/python3.14/platform.py", line 922, in from_subprocess
return subprocess.check_output(
~~~~~~~~~~~~~~~~~~~~~~~^
['uname', '-p'],
^^^^^^^^^^^^^^^^
...<2 lines>...
encoding="utf8",
^^^^^^^^^^^^^^^^
).strip()
^
File "/venv/lib/python3.14/site-packages/gevent/subprocess.py", line 418, in check_output
with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv/lib/python3.14/site-packages/gevent/subprocess.py", line 789, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pass_fds, cwd, env, universal_newlines,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...<5 lines>...
gid, gids, uid, umask,
^^^^^^^^^^^^^^^^^^^^^^
start_new_session, process_group)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv/lib/python3.14/site-packages/gevent/subprocess.py", line 1790, in _execute_child
data = errpipe_read.read()
File "/venv/lib/python3.14/site-packages/gevent/_fileobjectposix.py", line 167, in readall
data = self.__read(DEFAULT_BUFFER_SIZE)
File "/venv/lib/python3.14/site-packages/gevent/_fileobjectposix.py", line 161, in __read
wait_on_watcher(self._read_watcher, None, None, self.hub)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "src/gevent/_hub_primitives.py", line 326, in gevent._gevent_c_hub_primitives.wait_on_watcher
File "src/gevent/_hub_primitives.py", line 350, in gevent._gevent_c_hub_primitives.wait_on_watcher
File "src/gevent/_hub_primitives.py", line 304, in gevent._gevent_c_hub_primitives._primitive_wait
File "src/gevent/_hub_primitives.py", line 55, in gevent._gevent_c_hub_primitives.WaitOperationsGreenlet.wait
File "src/gevent/_waiter.py", line 154, in gevent._gevent_c_waiter.Waiter.get
File "src/gevent/_greenlet_primitives.py", line 65, in gevent._gevent_c_greenlet_primitives.SwitchOutGreenletWithLoop.switch
File "src/gevent/_gevent_c_greenlet_primitives.pxd", line 35, in gevent._gevent_c_greenlet_primitives._greenlet_switch
greenlet.GreenletExit
Tracer Version(s)
4.10.2
Python Version(s)
3.14.2
Pip Version(s)
24.x
Bug Report
Under gunicorn gevent workers + ddtrace, the entire gevent hub can freeze when
a greenlet forks a subprocess. In production this shows up as intermittent,
invisible worker stalls: the parked greenlet is eventually killed on worker
recycle, surfacing only as a
greenlet.GreenletExit.The trigger on our hot path is
platform.uname().processor, which lazily shellsout
uname -p. stripe-python takes this exact path when it builds theX-Stripe-Client-User-Agentheader on every request, so every outbound Stripecall is a potential fork.
With ddtrace==4.6.3 it is not reproducible in production.
Reproduction Code
No response
Error Logs
... TRUNCATED STACKTRACE
Libraries in Use
ddtrace==4.10.2
gevent==26.5.0
gunicorn (gevent worker class)
stripe (triggers platform.uname().processor on every request)
Operating System
Linux x86_64