From 06d9d2e7c7e71c5573a7c30db6e9c09191f4c197 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Wed, 16 Sep 2026 04:51:37 +0200 Subject: [PATCH] fix: sync Playwright loader spins a CPU core when a page opens a WebSocket (#30050) Fetching a single URL through the Playwright loader (fetch_url tool, a URL attached to a chat, the process/web endpoint) never returned when the page opened a WebSocket. The worker thread stayed at 100% CPU for the life of the process, and every further hit cost another core, so the whole instance got slow. Web search was unaffected, it uses the async loader. The sync loader's websocket route handler called the synchronous close(). Playwright runs websocket route handlers directly on its dispatcher fiber, so that call waited on the very loop it was blocking and busy-spun forever. The handler is now a no-op: a routed socket only reaches the network when the handler asks for it, so the page still cannot dial out, and nothing in the handler waits on the dispatcher any more. Aborting the upgrade request from the HTTP route handler instead does not work, page.route never sees WebSocket handshakes and the connection goes through. Fixes #30024 --- backend/open_webui/retrieval/web/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/retrieval/web/utils.py b/backend/open_webui/retrieval/web/utils.py index f16e7ff626..c2b8be5159 100644 --- a/backend/open_webui/retrieval/web/utils.py +++ b/backend/open_webui/retrieval/web/utils.py @@ -868,7 +868,8 @@ class SafePlaywrightURLLoader(BaseLoader, RateLimitMixin, URLProcessingMixin): browser.new_page(service_workers='block') as page, ): page.route('**/*', lambda route: self._intercept_navigation_sync(route, session)) - page.route_web_socket('**/*', lambda ws_route: ws_route.close()) + # sync close() hangs the dispatcher; a no-op handler still never connects to the server + page.route_web_socket('**/*', lambda ws_route: None) response = page.goto(url, timeout=self.playwright_timeout) if response is None: raise ValueError(f'page.goto() returned None for url {url}')