Use the async_playwright context manager to open a context.
Launch a new browser (only tested with Chromium).
Create a new page.
Within the async_playwright context create a task group, add at least two tasks, one should be a playwright task (i.e. page.<some_locator>.inner_text()).
Add a second task that raises an exception.
The exception will result in the task group being cancelled, which is not gracefully handled by playwright.
Expected behavior
I expected the task group to cancel the playwright task and than rereaise the original exception. This does happen, however, in cleaning up after cancellation playwright attempts to call set_exception on a future that is already cancelled.ror.
The problem appears to be in the cleanup method where set_exception is called on an already cancelled future. I verified this by putting a bunch of print statements in that particular code, and print(callback.fugure) printed <Future cancelled>. Calling set or set_exception an an already cancelled exception causes asyncio to raise a InvalidState error.
Actual behavior
Traceback (most recent call last):
File "/Users/tiemo/Workspace/playwright_bug/test.py", line 21, in <module>
asyncio.run(use_playwright())
File "/opt/homebrew/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 194, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/opt/homebrew/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/base_events.py", line 687, in run_until_complet
e
return future.result()
^^^^^^^^^^^^^^^
File "/Users/tiemo/Workspace/playwright_bug/test.py", line 11, in use_playwright
async with async_playwright() as sp:
File "/Users/tiemo/Workspace/playwright_bug/venv12/lib/python3.12/site-packages/playwright/async_api/_context_manager.py", line 57, in __aexit__
await self._connection.stop_async()
File "/Users/tiemo/Workspace/playwright_bug/venv12/lib/python3.12/site-packages/playwright/_impl/_connection.py", line 285, in stop_async
self.cleanup()
File "/Users/tiemo/Workspace/playwright_bug/venv12/lib/python3.12/site-packages/playwright/_impl/_connection.py", line 297, in cleanup
callback.future.set_exception(self._closed_error)
asyncio.exceptions.InvalidStateError: invalid state
Additional context
Below code reliably reproduces the error:
import asyncio
from playwright.async_api import async_playwright
async def raise_exception():
raise ValueError("Something went wrong")
async def use_playwright():
async with async_playwright() as sp:
browser = await sp.chromium.launch()
page = await browser.new_page()
await page.goto("https://example.com")
async with asyncio.TaskGroup() as group:
group.create_task(page.locator("css=h1").inner_text())
group.create_task(raise_exception())
asyncio.run(use_playwright())
Version
1.47.0
Steps to reproduce
async_playwright
context manager to open a context.async_playwright
context create a task group, add at least two tasks, one should be a playwright task (i.e.page.<some_locator>.inner_text()
).Expected behavior
I expected the task group to cancel the playwright task and than rereaise the original exception. This does happen, however, in cleaning up after cancellation playwright attempts to call
set_exception
on a future that is already cancelled.ror.The problem appears to be in the cleanup method where
set_exception
is called on an already cancelled future. I verified this by putting a bunch of print statements in that particular code, andprint(callback.fugure)
printed<Future cancelled>
. Callingset
orset_exception
an an already cancelled exception causes asyncio to raise aInvalidState
error.Actual behavior
Additional context
Below code reliably reproduces the error:
Environment