Webview 1 second delay on open, every time

Where does the bug appear (feature/product)?

Somewhere else…

Describe the Bug

Webview 1 second delay on open, every time

Steps to Reproduce

  1. Trigger new Webview (eg. with an extension like “Fast Fuzzy Finder”)
  2. New tab opens then takes ~1 second to render.

Expected Behavior

Webview / New Tab should load within milliseconds (like in VSCode).

Screenshots / Screen Recordings

Operating System

MacOS

Version Information

Version: 2.6.19
VSCode Version: 1.105.1
Commit: 224838f96445be37e3db643a163a817c15b36060
Date: 2026-03-12T04:07:27.435Z
Build Type: Stable
Release Track: Default
Electron: 39.4.0
Chromium: 142.0.7444.265
Node.js: 22.22.0
V8: 14.2.231.22-electron.0
OS: Darwin arm64 24.1.0

Additional Information

Console logs below.

Found unexpected service worker controller. Found: vscode-webview–18jebntdvmc8irfi8jm8eph6dn1dd3ihqjg3ojfr5el6067nugf5/service-worker.js?v=4&vscode-resource-base-authority=vscode-resource.vscode-cdn.net&id=20f78a4a-353f-4663-92a5-9b93f78735ed&remoteAuthority=. Expected: service-worker.js?v=4&vscode-resource-base-authority=vscode-resource.vscode-cdn.net&id=0fb16fb9-ba77-4b67-b155-f7cb9166912e&remoteAuthority=. Waiting for controllerchange.

Following the code path from the logs. Seems related to how Cursor handles controllerchange event:

					// At this point, either the service worker is ready and
					// became our controller, or we need to wait for it.
					// Note that navigator.serviceWorker.controller could be a
					// controller from a previously loaded service worker.
					const currentController = navigator.serviceWorker.controller;
					if (currentController?.scriptURL.endsWith(swPath)) {
						// service worker already loaded & ready to receive messages
						postVersionMessage(currentController);
					} else {
						if (currentController) {
							console.log(`Found unexpected service worker controller. Found: ${currentController.scriptURL}. Expected: ${swPath}. Waiting for controllerchange.`);
						} else {
							console.log(`No service worker controller found. Waiting for controllerchange.`);
						}

						// Either there's no controlling service worker, or it's an old one.
						// Wait for it to change before posting the message
						const onControllerChange = () => {
							navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);
							if (navigator.serviceWorker.controller) {
								postVersionMessage(navigator.serviceWorker.controller);
							} else {
								return reject(new Error('No controller found.'));
							}
						};
						navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
					}
				}).catch(error => {
					if (!onElectron && error instanceof Error && error.message.includes('user denied permission')) {
						return reject(new Error(`Could not register service worker. Please make sure third party cookies are enabled: ${error}`));
					}
					return reject(new Error(`Could not register service worker: ${error}.`));
				});

Does this stop you from using Cursor

No - Cursor works, but with this issue

Related

Debugging convo with Codex:

VS Code/Cursor registers an internal webview service worker using a URL like service-worker.js?..&id=.
The browser exposes the active controller as a fully resolved absolute URL, so it shows up as vscode-webview…/service-worker.js?..
The check is currentController.scriptURL.endsWith(swPath), so vscode-webview… vs service-worker.js is not the mismatch.
The mismatch is the querystring id: the page for webview 9b01… is still being controlled by the worker for webview 8379…
The key detail is that service worker control is based on scope, and the default scope comes from the script’s directory, not its querystring. MDN: the default scope is the directory where the service worker script is located. MDN register(). So two URLs like:

…/service-worker.js?..&id=A
…/service-worker.js?..&id=B
can still be in the same effective scope, while differing only by query params used as version/instance identity.

So what could be different in Cursor?

Most likely, one of these:

Cursor is keeping the previous webview controller alive longer than VS Code, so the new page starts under the old controller and waits for controllerchange.
Cursor has a webview/session/storage partition difference that delays service worker activation or takeover.
Cursor has a patch in its webview host/preload path that changes webview lifecycle timing enough to expose this race.

Hey, thanks for the detailed report and the great root cause analysis.

You’re right. At first this issue was tied to VSCode 1.99, and we updated the base version to 1.105.1. But like you found, the delay is still there for a different reason, service worker controller scope sharing between webviews in Electron.

I’ve passed this to the team along with your analysis. We don’t have a timeline yet, but your report, especially the breakdown of the specific code path, really helps with prioritization.

1 Like

thank you :folded_hands:

This topic was automatically closed 22 days after the last reply. New replies are no longer allowed.