GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — asyncio.wait leaks completed tasks via the 3.14 await-graph when racing a long-lived Future (missing `future_discard_from_awaited_by`) · Issue #152569 · python/cpython

asyncio.wait leaks completed tasks via the 3.14 await-graph when racing a long-lived Future (missing future_discard_from_awaited_by) #152569

New issue
Copy link
New issue
Copy link
Closed
Closed
asyncio.wait leaks completed tasks via the 3.14 await-graph when racing a long-lived Future (missing future_discard_from_awaited_by)#152569
Copy link
Labels
3.14bugs and security fixesbugs and security fixes3.15bugs and security fixesbugs and security fixes3.16new features, bugs and security fixesnew features, bugs and security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-asynciotype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@Skn0tt
Skn0tt
opened on Jun 29, 2026
Issue body actions

Bug report

Bug description:

On Python 3.14, asyncio.wait(..., return_when=FIRST_COMPLETED) permanently retains every
completed waiting task when one of the awaited futures is long-lived (never resolves). Each call
registers the waiting task on the long-lived future's await-graph (_asyncio_awaited_by) but never
discards it once the wait returns, so the task is pinned until that future finally resolves — which,
for a "signal" future used as a cancellation/close marker, can mean the lifetime of the process.

This is the same omission already fixed for asyncio.staggered.staggered_race in
gh-129195 (add to awaited_by → must discard), but asyncio.wait still has it.

import asyncio, gc

async def main():
    loop = asyncio.get_running_loop()
    immortal = loop.create_future()  # never resolves (e.g. a connection-close signal)

    def done_tasks():
        gc.collect()
        return sum(1 for o in gc.get_objects() if isinstance(o, asyncio.Task) and o.done())

    async def race_once():
        await asyncio.wait(
            {asyncio.ensure_future(asyncio.sleep(0)), immortal},
            return_when=asyncio.FIRST_COMPLETED,
        )

    base = done_tasks()
    for _ in range(1000):
        await asyncio.create_task(race_once())
    print("retained done tasks:", done_tasks() - base)
    immortal.cancel()

asyncio.run(main())
3.13: retained done tasks: 1
3.14: retained done tasks: 1000   # grows linearly with calls; freed only when `immortal` resolves

Related

  • await-graph feature: Async Call-Stack Reconstruction #91048 / PR GH-91048: Add utils for capturing async call stack for asyncio programs and enable profiling #124640
  • prior identical fix in stdlib: asyncio staggered race missing calls to asyncio.future_add_to_awaited_by() and asyncio.future_discard_from_awaited_by() #129195 (staggered_race missing discard)
  • surfaced as [Bug]: context.route retains completed Tasks until Connection close (memory leak) microsoft/playwright-python#3121

CPython versions tested on:

CPython main branch, 3.14

Operating systems tested on:

macOS

Linked PRs

  • gh-152569: Fix asyncio.wait leaking tasks via await-graph on long-lived futures #152585
  • [3.14] gh-152569: Fix asyncio.wait leaking tasks via await-graph on long-lived futures (GH-152585) #152866
  • [3.13] gh-152569: Fix asyncio.wait leaking tasks via await-graph on long-lived futures (GH-152585) #152867
  • [3.15] gh-152569: Fix asyncio.wait leaking tasks via await-graph on long-lived futures (GH-152585) #152868

Activity

  1. Skn0tt
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Jun 29, 2026
  2. picnixz
    added
    stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
    topic-asyncio
    3.14bugs and security fixesbugs and security fixes
    3.15bugs and security fixesbugs and security fixes
    3.16new features, bugs and security fixesnew features, bugs and security fixes
    on Jun 29, 2026
  3. github-project-automation
    added this to asyncioon Jun 29, 2026
  4. github-project-automation
    moved this to Todo in asyncioon Jun 29, 2026
  5. kumaraditya303
    added 3 commits that reference this issue on Jul 2, 2026

    gh-152569: Fix asyncio.wait leaking tasks via await-graph on long-liv…

    f8514dc

    [3.14] gh-152569: Fix asyncio.wait leaking tasks via await-graph on l…

    86e5a6e

    [3.15] gh-152569: Fix asyncio.wait leaking tasks via await-graph on l…

    c2cb9bd
  6. kumaraditya303
    closed this as completedon Jul 2, 2026
  7. github-project-automation
    moved this from Todo to Done in asyncioon Jul 2, 2026
  8. gopaljigaur
    added a commit that references this issue on Oct 15, 2026

    fix(mcp): keep no finished tool callers alive on Python 3.14.0 to 3.14.6

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.14bugs and security fixesbugs and security fixes3.15bugs and security fixesbugs and security fixes3.16new features, bugs and security fixesnew features, bugs and security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-asynciotype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    • asyncio
      Status
      Done
      Show more project fields

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.