Skip to content

[Bug]: Sync API — pw.stop() raises "This event loop is already running" at teardown after 1.64 sync-dispatcher rework (#3230) #3307

Closed

Description

@yuliagalon

Version

playwright 1.64.0 and 1.64.1, Python 3.12, Linux, sync API under pytest + pytest-xdist.

Summary

After upgrading 1.59 → 1.64, tearing down a session-scoped sync Playwright intermittently raises RuntimeError: This event loop is already running from pw.stop(). The user code completed successfully; only teardown fails.

Stack

pw.stop()  -> playwright/sync_api/_context_manager.py __exit__
  -> Connection.stop_sync()  (playwright/_impl/_connection.py:365)
    -> self._loop.run_until_complete(self._transport.wait_until_stopped())
      -> asyncio base_events _check_running -> RuntimeError: This event loop is already running

Analysis

Connection.stop_sync() is byte-identical between 1.59 and 1.64. The behavioural change is in sync_api/_context_manager.py::greenlet_main + the new _impl/_greenlets.py::wait_for_future helper introduced in #3230. At teardown the dispatcher fiber's run_until_complete(task) can still be on the stack (so loop._thread_id is still set) when stop_sync() runs its own run_until_complete(...), so _check_running() trips.

Repro conditions

  • sync API, long-lived session-scoped Playwright, pytest-xdist workers.
  • Intermittent, markedly more frequent against slow / high-latency backends.
  • Not triggered by a signal or a timeout: it occurs with pytest-timeout using --timeout-method=thread and the test finishing well under the limit, so no signal reaches the main thread. (This is distinct from the signal-handler scenario fix(sync): survive an exception raised by a signal handler during a sync call #3230's title describes.)
  • We don't yet have a deterministic standalone minimal repro (it's timing-dependent), but it reproduces at a high rate on slower environments. Happy to test a proposed fix.

Question

Is this a known regression from #3230? What is the intended teardown contract when the dispatcher fiber is still live at stop_sync()?

Activity

  1. github-actions commented on Nov 3, 2026

    @github-actions
  2. microsoft-playwright-automation commented on Nov 3, 2026

    @microsoft-playwright-automation
    Contributor

    Hi, I'm the Playwright bot. I reproduced this and prepared a fix.

    Confirmed regression from #3230. On Linux with Python 3.12.15, a completed event wait leaves a queued callback that resumes user code during pw.stop(), before the dispatcher exits. No signal or xdist is needed. The fix makes that callback inactive when its wait ends, including callbacks already queued by asyncio.

    Minimal repro and verification
    from playwright.sync_api import sync_playwright
    
    pw = sync_playwright().start()
    browser = pw.chromium.launch()
    page = browser.new_page()
    with page.expect_console_message() as info:
        page.evaluate("console.log('hello')")
    assert info.value.text == "hello"
    pw.stop()

    The same script passes on 1.63.0 (Chromium 153.0.8010.12) and fails on both 1.64.0 and 1.64.1 (Chromium 156.0.8078.4), at the reported Connection.stop_sync() call.

    On main at 0fcb2ee2eb87cc630ff079208199afd7205d6fb8, with driver commit eac236da288cdc3d0891278a61e1a876348950a0, the regression test fails on Chromium, Firefox and WebKit. The newer shutdown path raises the same error at shutdown_asyncgens().

    The test passes on all three browsers with the fix. The neighbouring sync checks pass under three xdist workers: 125 passed, one existing upstream skip. The corresponding async checks pass: 62 passed, the same skip. mypy playwright and ./scripts/lint.sh pass.

    stop() must let the dispatcher finish before it runs loop cleanup. The fault is in the callback lifetime in wait_for_future(), not in Connection.stop_sync() or pytest-timeout.

    Draft PR: #3311

    Triaged on Linux by the Playwright bot - agent run

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions