Skip to content

fix Future exception never retrieved in create_connection - #590

Open
jensbjorgensen wants to merge 1 commit into
MagicStack:masterfrom
jensbjorgensen:getaddrinfo_future_unretrieved
Open

jensbjorgensen wants to merge 1 commit into
MagicStack:masterfrom
jensbjorgensen:getaddrinfo_future_unretrieved

Conversation

@jensbjorgensen

@jensbjorgensen jensbjorgensen commented Jan 17, 2024 •

Copy link
Copy Markdown
Contributor

I also have observed that when I use asyncio.wait_for(...) around loop.create_connection in the timeout case I end up getting this logged to stdout(err?):

Future exception was never retrieved
future: <Future finished exception=gaierror(-2, 'Name or service not known')>
socket.gaierror: [Errno -2] Name or service not known

The code in this commit fixes this.

@fantix fantix left a comment

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.

This makes sense! Though a unit test is preferred.

Comment thread uvloop/loop.pyx Outdated
except asyncio.CancelledError as exc:
for fut in fs:
fut.cancel()
raise exc from 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.

Why dropping the original context here, instead of just raise?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Looks like I failed to loop back to this and take care of your requests. Just pushed a commit (replaced old one since it was now behind), I now just raise and I've also added a unit test.

@HardMax71

Copy link
Copy Markdown

Still happens on 0.22.1 and 0.23.0. Repro, the lookup fails after the caller has given up:

import asyncio, gc, logging, sys
import uvloop

logging.basicConfig(level=logging.ERROR)


async def main():
    loop = asyncio.get_running_loop()
    try:
        await asyncio.wait_for(loop.create_connection(asyncio.Protocol, "no-such-host.invalid", 443), timeout=0.001)
    except (asyncio.TimeoutError, OSError) as e:
        print("caller sees:", type(e).__name__)
    await asyncio.sleep(2)  # let the lookup fail
    gc.collect()
    await asyncio.sleep(0.1)


if sys.argv[1:] == ["stdlib"]:
    asyncio.run(main())
else:
    uvloop.run(main())

0.23.0:

ERROR:asyncio:Future exception was never retrieved
future: <Future finished exception=gaierror(8, 'nodename nor servname provided, or not known')>
socket.gaierror: [Errno 8] nodename nor servname provided, or not known
caller sees: TimeoutError

With the stdlib loop (python repro.py stdlib) only caller sees: TimeoutError is printed.

I applied this PR's diff on top of v0.23.0 (it also still applies to current master) and built it: the warning is gone, also under -X dev, and tests/test_dns.py passes (57 tests). We get this logged from uvicorn workers every time DNS has a short outage, so it would be nice to have it merged.

When create_connection() is cancelled (e.g. by a wait_for() timeout)
while its getaddrinfo() lookups are still in flight, asyncio.wait()
leaves the lookup futures running.  Once such a lookup fails, nobody
retrieves its exception and the loop logs:

    Future exception was never retrieved
    future: <Future finished exception=gaierror(-2, 'Name or service not known')>

Cancel the pending lookups when the wait is cancelled.  A lookup that
has already finished can't be cancelled, so mark its exception (if any)
as retrieved instead.

The stdlib event loop doesn't have this problem because its lookup
future is awaited directly and therefore cancelled with the task.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@jensbjorgensen
jensbjorgensen force-pushed the getaddrinfo_future_unretrieved branch from d464c58 to f8e3a70 Compare October 6, 2026 13:35

@jensbjorgensen jensbjorgensen left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

switched to raise, unit test added

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants