-
-
Notifications
You must be signed in to change notification settings - Fork 8
Description
Subject
This is a followup to our conversation at thomas-xin/Miza@d1a9573#r165312062 - While the original bug appears to be fixed, the current state of the library appears to very occasionally cause deadlocks instead.
Environment
OS Windows-11-10.0.26220-SP0
Python 3.13.8
OpenSSL 3.0.18 30 Sep 2025
urllib3 2.14.906
Steps to Reproduce
I believe I can't give exact steps to reproduce, as this is an inconsistent issue that gradually builds up "leaked" stuck tasks as more requests are performed. The only thing that remains consistent is that it occurs with session reuse.
To be clear, I am invoking the request via niquests and not urllib3-future directly, but I have traced the problematic sections to this library, as seen below. Note that due to the nature of this problem, there is no naturally occurring exception or traceback to be shown.
Expected Behavior
Requests should either complete correctly, give a relevant error, or produce a timeout.
Even if timeout does not occur for any reason, it should remain cancellable at any point.
Actual Behavior
The task never finishes despite the request being given a timeout, remaining in pending state.
If wrapped with asyncio.wait_for, after that timeout expires, the task becomes "cancelling" but never exits that state either. This also can occur if the request is cancelled directly with .cancel().
When probing its properties, something similar to the following can be found:
<Task cancelling name='Task-680' coro=<idle_conn_watch_task() running at ...\Python313\site-packages\urllib3\_async\connectionpool.py:171> wait_for=<Future pending cb=[Task.task_wakeup()] created at ...\Python313\Lib\asyncio\base_events.py:459> created at ...\Python313\Lib\asyncio\tasks.py:410>
When issuing traceback.print_stack, I will typically see the following line:
File "...\Python313\site-packages\urllib3\_async\connectionpool.py", line 171, in idle_conn_watch_task await asyncio.sleep(waiting_delay)
And finally, when inspecting the .cancelling() method of the task, I will typically see a very large number in the hundreds, such as 715. This perhaps implies something is swallowing asyncio.CancelledError, however I have not been able to find the cause from the source code itself.
I hope this information is still useful, however!