Skip to content

Remove asyncio.shield in _drain_helper to fix unhandled future warnings#12293

Open
joaquinhuigomez wants to merge 3 commits intoaio-libs:masterfrom
joaquinhuigomez:fix/drain-helper-shield-removal
Open

Remove asyncio.shield in _drain_helper to fix unhandled future warnings#12293
joaquinhuigomez wants to merge 3 commits intoaio-libs:masterfrom
joaquinhuigomez:fix/drain-helper-shield-removal

Conversation

@joaquinhuigomez
Copy link
Copy Markdown

Replace asyncio.shield(waiter) with direct await waiter in _drain_helper.

HTTP/1.1 has a single consumer per drain waiter, so the shield is unnecessary. When a handler task is cancelled during write backpressure, asyncio.shield keeps the original waiter alive and uncancelled. connection_lost then calls set_exception on it, but nobody is awaiting it anymore, producing noisy "Future exception was never retrieved" warnings.

Without the shield, cancellation propagates directly to the waiter. connection_lost finds waiter.done() == True and skips set_exception.

Includes a regression test that verifies the waiter is properly cancelled (not left with an unhandled exception) when the task is cancelled during backpressure and connection_lost fires afterward.

Closes #12281

…rnings

Replace asyncio.shield(waiter) with direct await on waiter in
_drain_helper. HTTP/1.1 has a single consumer per drain waiter,
so shield is unnecessary and causes "Future exception was never
retrieved" warnings on cancellation.

Closes aio-libs#12281
@psf-chronographer psf-chronographer bot added the bot:chronographer:provided There is a change note present in this PR label Mar 28, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.10%. Comparing base (5ed2e12) to head (5cf4a25).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #12293      +/-   ##
==========================================
- Coverage   99.11%   99.10%   -0.01%     
==========================================
  Files         130      130              
  Lines       45446    45468      +22     
  Branches     2398     2398              
==========================================
+ Hits        45044    45063      +19     
- Misses        272      275       +3     
  Partials      130      130              
Flag Coverage Δ
CI-GHA 98.96% <100.00%> (-0.01%) ⬇️
OS-Linux 98.70% <100.00%> (-0.01%) ⬇️
OS-Windows 96.98% <100.00%> (-0.01%) ⬇️
OS-macOS 97.87% <100.00%> (+<0.01%) ⬆️
Py-3.10.11 97.42% <100.00%> (+<0.01%) ⬆️
Py-3.10.20 97.89% <100.00%> (+<0.01%) ⬆️
Py-3.11.15 98.10% <100.00%> (+<0.01%) ⬆️
Py-3.11.9 97.63% <100.00%> (+<0.01%) ⬆️
Py-3.12.10 97.72% <100.00%> (+<0.01%) ⬆️
Py-3.12.13 98.19% <100.00%> (+<0.01%) ⬆️
Py-3.13.12 98.44% <100.00%> (-0.01%) ⬇️
Py-3.14.3 98.50% <100.00%> (-0.01%) ⬇️
Py-3.14.3t 97.50% <100.00%> (-0.01%) ⬇️
Py-pypy3.11.15-7.3.21 97.53% <100.00%> (-0.01%) ⬇️
VM-macos 97.87% <100.00%> (+<0.01%) ⬆️
VM-ubuntu 98.70% <100.00%> (-0.01%) ⬇️
VM-windows 96.98% <100.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 28, 2026

Merging this PR will not alter performance

✅ 59 untouched benchmarks


Comparing joaquinhuigomez:fix/drain-helper-shield-removal (5cf4a25) with master (5ed2e12)

Open in CodSpeed

mypy flags the final assertion as unreachable after the suppress(CancelledError)
context manager. The code IS reachable at runtime — the suppress catches the
CancelledError from the cancelled task, and execution continues normally.
assert pr._drain_waiter is None


async def test_cancelled_drain_no_unhandled_future_warning() -> None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but I don't feel this test is validating aiohttp's behaviour. It may or may not match aiohttp's implementation at a given point in time.

As mentioned in the issue, I'd like to see a full functional test which shouldn't be too difficult given the reporter's reproducer and the example test I linked to.

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

Labels

bot:chronographer:provided There is a change note present in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Future exception was never retrieved" spam when request handler is cancelled during write backpressure

2 participants