Repository navigation
[Bug] Webview bootstrap blocks on Service Worker update after the expected controller is ready #8073
Description
Existing issues
- I searched existing issues and PRs for the explicit registration/update wait. I did not find a matching report.
Summary
In this Docker deployment, extension webview initialization blocks on an explicit await registration.update() for 45.83 seconds. The expected webview worker takes control approximately 5 ms after the update starts, but content initialization still waits for the update promise. Codex's 30-second renderer watchdog then expires. A second webview's registration also queues for approximately 45.73 seconds. Project Manager was affected as well.
This is separate from a newer Codex preload loop that also froze the entire editor UI. The preload problem is documented in the separate extension investigation below. Bounding the Service Worker update alone did not fix that UI freeze.
OS/Web Information
- Observed: October 24, 2026.
- LinuxServer Docker image:
4.143.1-ls373; build time2026-10-24T01:07:31+00:00. - code-server:
4.143.1, commit22ddf60dbc52f164cff13620ab515086dd08c90a; Code1.143.1. - Previous published image:
4.141.1-ls372. - Browser used for measurements: Chrome for Testing
154.0.8037.97, Linux, Playwright. - HTTPS deployment behind nginx, with a root PWA Service Worker.
- Codex
openai.chatgpt: currently26.1021.22027; older26.1007.21434was also tested during diagnosis. - Project Manager:
13.1.1. - The deployment has UI customizations; their contribution has not been fully isolated.
I have not reproduced the scheduling delay in a clean official image or established a browser-independent regression.
Steps to Reproduce (observed deployment)
- Start an authenticated browser context with empty Service Worker/cache state.
- Open code-server and extension webviews such as Codex and Project Manager.
- Record registration, explicit update, controller changes, and webview performance marks.
- The editor shell is available in approximately 2–5 seconds, while webview initialization remains before
content/workerReady. - Codex reports
renderer_ready_timeoutat approximately 30 seconds.
Blocking logic
Generated file:
lib/vscode/out/vs/workbench/contrib/webview/browser/pre/index.html
Relevant original logic:
navigator.serviceWorker.register(swPath).then(async registration => {
if (navigator.serviceWorker.controller) {
registration = await registration.update();
}
// Wait for controller changes before resolving workerReady.
});The initial controller in the measured frame was the root PWA worker, /_static/out/browser/serviceWorker.js. Checking for any controller therefore starts another update while the webview worker is still installing.
| Event | Time from webview initialization |
|---|---|
register() resolves, installing worker present |
494.4 ms |
Explicit update() starts |
495.0 ms |
| Expected webview worker takes control | 499.8 ms |
update() resolves |
46,325.4 ms |
The update waits 45.83 seconds. The reason for the browser's long update-job scheduling remains unproven. A second webview's register() waits approximately 45.73 seconds, so bounding only the explicit update does not address all observed queueing.
Representative log:
Webview renderer did not become ready elapsedMs=30002 extensionVersion=26.1021.22027 reason=renderer_ready_timeout receivedWebviewMessage=false role=sidebar stage=renderer timeoutMs=30000
Was this introduced by the latest code-server update?
The operator reports that both the previous and current images open the old Codex, and both fail after Codex is updated. This is the operator's A/B observation, not an independently repeated full-image A/B test.
Source comparisons found:
await registration.update()is present in VS Code1.141.0,1.141.1, and1.143.1.- Their
src/vs/workbench/contrib/webview/browser/pre/service-worker.jsfiles are byte-identical: SHA-25683c76adde3ba663433523660289f99111bf00f395ef541d567c060a6f7cc7d8e. - code-server
v4.141.1...v4.143.1changes topatches/webview.diffare context line-number changes. - Other webview host changes exist, including mounting/key-event handshake changes; the whole older bootstrap should not be copied onto the newer host.
These findings support an existing shared bootstrap weakness exposed alongside the Codex update, rather than a newly added update() wait in this code-server release. They do not exclude every possible backend regression.
Primary source references:
https://github.com/coder/code-server/compare/v4.141.1...v4.143.1
https://github.com/linuxserver/docker-code-server/releases/tag/4.141.1-ls372
https://github.com/linuxserver/docker-code-server/releases/tag/4.143.1-ls373
https://github.com/microsoft/vscode/blob/1.141.1/src/vs/workbench/contrib/webview/browser/pre/index.html
https://github.com/microsoft/vscode/blob/1.143.1/src/vs/workbench/contrib/webview/browser/pre/index.html
https://github.com/microsoft/vscode/blob/1.143.1/src/vs/workbench/contrib/webview/browser/pre/service-worker.js
Local workaround and proposed direction
The original local code-server-webview-timeout.patch is a diagnostic workaround against this deployment's generated files (the source-level PR is narrower and does not introduce the localStorage setting):
- Reuse a registration only when its exact scope and complete active script URL match the expected worker.
- Register normally when no usable matching worker exists.
- Bound the explicit update wait to 3,000 ms by default, continuing with the matching active worker on timeout or update failure. The browser update job itself is not canceled.
- Still require the expected worker to control the iframe. Request a narrowly validated
clients.claim()when necessary; controller acquisition has a separate 10-second failure timeout. - Update the inline CSP hash without relaxing the CSP.
Local setting, in the code-server origin's browser console:
localStorage.setItem('code-server.webview.swUpdateTimeoutMs', '3000');Reload after changing it. Values from 0 to 60,000 ms are accepted; invalid values default to 3,000. This is a local patch setting, not an upstream option or an environment variable. It bounds only the update wait when a matching active worker exists. It does not limit initial registration or total startup time, and timers need a responsive event loop.
Validation and limits
- The shared bootstrap/worker changes were checked separately for a hanging update, controller acquisition, initial registration, syntax, and CSP consistency.
- The final local deployment also skips Codex's pathological
modulepreloadinsertion and batches unmodified startup assets into a worker-side cache. Those extension-specific mitigations are not included in the focused timeout patch. - With the combined workaround, cold-browser tests reached the Codex welcome screen in approximately 31–44 seconds. During an actual served-page run, opening the command palette took 256 / 151 / 804 ms, with the longest sampled main-frame evaluation taking 755 ms.
- The combined fix restores UI responsiveness; cold startup is still slow. Successful combined tests do not prove that the Service Worker patch alone fixes all Codex startup problems.
- A final actual served-page run reached the welcome screen at 43,738 ms and an editable composer before the interaction test ended at 53,660 ms. Menu opens took 420 / 157 / 588 ms; the longest main-frame evaluation was 939 ms. No chat request was submitted as part of validation.
Would matching-registration reuse and a bounded update wait be appropriate in this bootstrap? Should this be addressed in code-server or VS Code upstream, and what browser tracing would help identify the long update scheduling delay?
Independent extension preload stall (additional investigation)
This section records a separate bottleneck for triage; the shared-Service-Worker PR does not claim to fix it. The CPU-profile findings and deployed cold-start timings below are from the supplied diagnostic notes. The underlying CPU profile, raw timing traces, full image runtime A/B, and cache-rebuild watcher source were not included in the review bundle.
Codex preload investigation and limits
Summary
After updating Codex, opening its sidebar can freeze the entire code-server page, including editor menus. CPU profiling identifies approximately 20,020 ms in the webview preload helper while it repeatedly scans a growing live collection of <link> elements. The new startup dependency map contains 3,203 dependencies.
There is also an independent shared webview Service Worker update wait, described above. Fixing that wait alone did not remove the UI freeze.
Environment and version comparison
- Date: October 24, 2026.
- Codex:
openai.chatgpt26.1021.22027, Linux x64. - Older Codex reported working by the operator:
26.1007.21434. - code-server:
4.143.1, Code1.143.1, LinuxServer image4.143.1-ls373. - Previous image:
4.141.1-ls372. - Diagnostic browser: Chrome for Testing
154.0.8037.97, Linux, Playwright. - HTTPS, nginx, remote webview resource transport.
The operator reports the same matrix on both images: old Codex works; updating Codex triggers the failure. This is operator-provided evidence. A full independent old-image runtime A/B was not performed.
Relevant assets and profile
webview/assets/index-7e47869603c2.js
webview/assets/app-main-765fabf054a6.js
webview/assets/app-initial-13f932cb2f13.js
The main startup map has 3,203 dependencies, totaling 73,088,572 bytes of source assets. Including the entry and additional entry dependency yields 3,205 cached files and 73,228,948 source bytes. These are source-file counts/bytes, not a claim that every file is fetched independently in every browser run.
The helper obtains a live collection and scans it for each dependency while appending more links. Simplified observed structure:
const links = document.getElementsByTagName('link');
for (const dependency of dependencies) {
for (let i = links.length - 1; i >= 0; i--) {
// Check whether this dependency already has a link.
}
document.head.appendChild(modulePreloadLink);
}The growing live collection makes repeated scans increasingly expensive. About 3,198 modulepreload links were inserted in the observed run.
CPU profile findings:
- Approximately 20,020 ms attributed to
app-initial-13f932cb2f13.js, line 1, column 11,593 in the captured profile. - Approximately 1,708 ms additionally attributed to
appendChild. - Skipping modulepreload link insertion removes the observed 20-second hotspot in a comparison profile.
The old version's main entry, app-main-92173aa1395c.js, is a 539-byte initialization wrapper with direct imports. This comparison does not establish the old version's total transitive module count or imply it has no dependencies.
Observed behavior
- Open a fresh authenticated code-server browser context.
- Open the Codex sidebar in an extension-enabled workspace.
- Observe a long preload loop and the workbench becoming unresponsive, including menus.
- In some runs the 30-second renderer watchdog reports
renderer_ready_timeout.
The diagnostic deployment uses custom workbench/UI code, and this has not yet been reproduced in a clean desktop VS Code or clean official code-server image. The profile directly identifies the expensive loop in this deployment; it does not establish why the browser schedules all frames together or that every browser has the same freeze.
Temporary mitigation and validation
The local mitigation is outside the extension installation, so installing another Codex version does not overwrite it:
- Suppress only
LINKnodes withrel === 'modulepreload'in the Codex inner document. Normal module imports still load. - Keep original JavaScript/CSS bytes and module URLs. No rebundling or extension-file edits remain.
- Batch the current startup dependency assets into a gzip pack; the webview Service Worker serves the original module URLs from an in-memory cache. This reduces remote per-resource transport overhead.
- Watch extension registry changes and rebuild the pack for updated versions using the same supported asset layout. Unknown layouts fail back to normal resource loading.
A candidate cold-browser test reached the welcome UI at 31,077 ms, completed the onboarding screens, and reached an editable composer. Command-palette opens during loading took 350 / 909 / 253 ms, with the longest sampled main-frame evaluation 550 ms. A separate actual served-page run reached the welcome UI at 37,838 ms, with command-palette opens 256 / 151 / 804 ms.
This restores responsiveness but does not make cold startup fast. Tests used an empty extension-enabled workspace; a fresh browser opening an untrusted folder has a separate Restricted Mode activation constraint. No prompt was submitted, and backend generation was not tested.
Could the preload helper use a Set of existing URLs or another bounded lookup rather than repeatedly traversing a live collection? Can the main startup dependency graph be narrowed or loaded incrementally to avoid thousands of preload insertions and the renderer startup deadline?
A final actual served-page test reached the welcome UI at 43,738 ms and an editable composer before the interaction test ended at 53,660 ms. Menu opens took 420 / 157 / 588 ms, with the longest sampled main-frame evaluation 939 ms. Cold loading therefore varied across runs; no sub-30-second startup guarantee is claimed.
Checks performed while preparing this report
- Independently fetched VS Code 1.141.0, 1.141.1 and 1.143.1 sources: all contain
await registration.update(), and all three webview worker SHA-256 hashes match the value above. - Independently checked the code-server version comparison: the
patches/webview.diffchanges are hunk line-number updates. Both LinuxServer release tags exist. - Added real-Chromium regression coverage against the pinned VS Code source bootstrap and actual Service Worker implementation. This isolates worker lifecycle behavior without depending on extension activation or a compiled workbench.
- The unpatched code-server bootstrap fails the root-controller/hanging-update regression. The proposed source patch passes the browser cases. These checks do not reproduce the original 45.83-second browser scheduling delay or the 20-second extension preload profile.
Remaining template information
- Local diagnostic OS: Linux; distribution and remote CPU architecture were not recorded in the supplied notes.
- Native VS Code: not tested.
- VS Code web (
code serve-web): not tested. - GitHub Codespaces: not tested.
- Accessed over a secure context (HTTPS through nginx).
The deployment-specific asset cache and Node.prototype.appendChild interception have been excluded from the proposed upstream code. They depend on extension internals and deployment paths and require separate design and validation.
Proposed source-level fix
PR #8074 addresses the shared Service Worker bootstrap wait and controller acquisition. It excludes the independent Codex preload suppression and deployment-specific module cache.
Activity
linear-code commentedon Oct 24, 2026
CODE-87
fix(webview): bound worker updates and acquire the expected controller