You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Bug] Webview-heavy extensions render blank on Safari/iPad — VSBuffer.slice returns view that gets detached by extension-host postMessage transfer #7801
Web Browser: Safari 18.x on macOS / iPadOS (reproduces). Chrome 14x on macOS (does NOT reproduce). Bug is WebKit-specific.
Local OS: iPadOS 18 / macOS 15 (clients)
Remote OS: Ubuntu 24.04 (Docker container running on a private VM)
Remote Architecture: amd64
code-server --version: 4.106.0 99fef9c... (vendored VS Code 1.107.x)
Steps to Reproduce
Install code-server on a Linux host. Front it with HTTPS (real TLS via a
reverse proxy, mkcert, or code-server --cert) — a secure context is
required for the service-worker layer where this bug surfaces.
Install a webview-heavy extension. Easiest reliable repro is Claude Code
from Open VSX (Anthropic.claude-code, version 2.1.77 specifically — newer
versions hit an UNRELATED Anthropic-side regression that masks this bug;
2.1.77 is the last release where the extension activates cleanly so this
bug is fully exposed):
Alternative repros that don't require an Anthropic account: any extension
that delivers initial webview content as multi-chunk binary IPC frames —
vscode-pdf (open any PDF), .ipynb notebook renderers (open a notebook
with rendered output cells), Live Preview, etc. Less deterministic but
triggers the same code path.
Open code-server in Safari 18 on Mac or iPad. (Or any iOS browser —
Chrome/Brave/Firefox on iOS are all forced WebKit by App Store policy.)
Open Web Inspector → Console.
Click the extension's activity-bar icon to open its webview panel.
Observe blank panel + TypeError: Underlying ArrayBuffer has been detached from the view or out-of-bounds cascade in the console.
Confirm it does NOT reproduce on Chromium: open the same code-server URL
in Chrome/Chromium/Brave/Edge on macOS/Linux/Windows. Same extension,
same workspace, panel renders normally.
Expected
The extension's webview panel renders normally — same as on Chromium
browsers, same as on native VS Code desktop.
Actual
The panel slot opens but stays permanently empty (no content, no error UI,
no spinner — just the empty letterpress background). The workbench console
fires a cascade of:
TypeError: Underlying ArrayBuffer has been detached from the view or out-of-bounds
Uint8Array.prototype.slice
VSBuffer.prototype.slice (out/vs/base/common/buffer.js)
ChunkStream._read (out/vs/base/parts/ipc/common/ipc.net.js)
ProtocolReader.onRawMessage / acceptChunk
…one per IPC chunk on the management socket, then one per chunk on the
extension-host socket. The cascade kills the message channel; any
subsequent IPC traffic for that extension is dead until reload.
ROOT CAUSE (short version — the structural fix is upstream in
microsoft/vscode, but the bug surfaces in practice via code-server because
code-server is browser-only):
VSBuffer.prototype.slice in vs/base/common/buffer.ts was changed in microsoft/vscode#76076 from a copying slice() to a non-copying subarray() for performance. That returns a Uint8Array VIEW into the
underlying ArrayBuffer, not a copy.
The view ends up queued inside ChunkStream while the IPC layer is reading
multi-chunk messages. Downstream, the extension-host iframe receives those
messages via postMessage(msg, [transferList]) — which DETACHES the
underlying ArrayBuffer. On V8 (Chromium/Node/Electron), subsequent Uint8Array.prototype.slice calls on the now-detached view are permissive
(historically returned empty buffer; modern V8 throws but the detach
timing is loose enough that the cascade rarely fires). On JavaScriptCore
(Safari, all iOS browsers), the throw is immediate and unconditional per
TC39 ECMA-262 §25.1.1.7. The IPC reader unwinds, fires handleUnexpectedError, and the channel is dead.
Native VS Code (Electron) doesn't trip this because its IPC doesn't use
browser postMessage with transfer lists for VSBuffer payloads. That's why
the bug is invisible on desktop and only surfaces in browser-mode VS Code.
PROPOSED ACTION FOR code-server:
The minified bundle of workbench.js ships verbatim from upstream and
contains the exact line slice(i,e){return new a(this.buffer.subarray(i,e))}
inside the VSBuffer class. Changing subarray to slice (single character)
fixes the bug by forcing a copy — the new buffer is owned by no one else, so
nothing downstream can detach it.
I've been running this single-character patch in production for ~24h with
no observed regression. Suggested implementation: add a patch to patches/ (e.g. vsbuffer-slice-webkit.diff) that touches src/vs/base/common/buffer.ts:
return new VSBuffer(this.buffer.subarray(start, end));
return new VSBuffer(this.buffer.slice(start, end));
That fits the established pattern of other patches you carry against
upstream (webview.diff, service-worker.diff, signature-verification.diff,
etc.). Performance cost is negligible — one extra memcpy per
ChunkStream chunk-split, sub-kilobyte typical message sizes.
I'm happy to open the PR if you'd like to carry this temporarily; or to
just keep this issue open as a downstream tracker until upstream merges
the structural fix in microsoft/vscode (which I'm filing separately).
Filing here, despite this being technically an upstream bug, because:
The user-facing population that hits this is overwhelmingly
code-server users (every Safari/iPad/iOS user of any non-trivial
code-server deployment).
The structural fix in microsoft/vscode reverses a deliberate
performance optimization (#76076) and will likely need design
discussion before it lands, so a downstream patch unblocks affected
users in the meantime.
Logs
Server (code-server --verbose) — relevant lines around the failing IPC
exchange. The server side is healthy; the failure is entirely client-side
inside the browser's IPC deserializer: info Wrote certificate to /etc/letsencrypt/... info HTTPS server listening on https://0.0.0.0:8080/ info HTTP/2 enabled debug protocol Initiating handshake... {"token":"…"} debug protocol Handshake completed {"token":"…"} debug management Connecting... {"token":"…"} debug exthost Connecting... {"token":"…"} debug exthost Handshake completed {"token":"…"} debug exthost Sending socket {"token":"…"} (no error on the server side — failure surfaces only in the browser)Browser console (Safari 18 on iPad, Web Inspector attached) — full outputfrom clicking the extension activity-bar icon. Everything up through"Extension activated!" is normal activation; the ArrayBuffer errorsfollow immediately: [Log] INFO – "Resolving connection token (…)..." [Log] INFO – "Creating a socket (renderer-Management-…)..." [Log] INFO – "[reconnection-grace-time] Client received grace time from server: 10800000ms (10800s)" [Warning] The web worker extension host is started in a same-origin iframe! [Log] INFO – "Creating a socket (renderer-ExtensionHost-…)..." [Log] INFO – "[Service Worker] registered" [Log] [Extension Host] Extension activated! [Error] ERR – "Underlying ArrayBuffer has been detached from the view or out-of-bounds: subarray@[native code] slice@…/workbench.js:406:97282 read@…/workbench.js:1032:107875 A9e@…/workbench.js:1032:108819 onRawMessage@…/workbench.js:1032:109928 … acceptChunk@…/workbench.js:1034:6504 …" [Error] ERR – "Underlying ArrayBuffer has been detached from the view or out-of-bounds: subarray@[native code] slice@…/workbench.js:406:97282 _read@…/workbench.js:1034:4585 acceptChunk@…/workbench.js:1034:5868 …" …repeats once per binary IPC chunk…Confirming the offending line in the running workbench bundle: $ docker exec <container> bash -c \ "sed -n 406p /usr/lib/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.js \ | cut -c 96900-97500" … constructor(i){this.buffer=i,this.byteLength=this.buffer.byteLength} clone(){let i=a.alloc(this.byteLength);return i.set(this),i} toString(){…} slice(i,e){return new a(this.buffer.subarray(i,e))} ← THE BUG set(i,e){…} …Network tab: WebSocket /vscode-remote-resource/… is connected and exchangingbinary frames normally before the error fires. The transport is healthy —the bug is in how the client-side parses the frames.
Screenshot/Video
No response
Does this bug reproduce in native VS Code?
No, this works as expected in native VS Code
Does this bug reproduce in VS Code web?
I did not test VS Code web
Does this bug reproduce in GitHub Codespaces?
Yes, this is also broken in GitHub Codespaces
Are you accessing code-server over a secure context?
Is there an existing issue for this?
OS/Web Information
Steps to Reproduce
Install code-server on a Linux host. Front it with HTTPS (real TLS via a
reverse proxy, mkcert, or
code-server --cert) — a secure context isrequired for the service-worker layer where this bug surfaces.
Install a webview-heavy extension. Easiest reliable repro is Claude Code
from Open VSX (Anthropic.claude-code, version 2.1.77 specifically — newer
versions hit an UNRELATED Anthropic-side regression that masks this bug;
2.1.77 is the last release where the extension activates cleanly so this
bug is fully exposed):
Alternative repros that don't require an Anthropic account: any extension
that delivers initial webview content as multi-chunk binary IPC frames —
vscode-pdf (open any PDF), .ipynb notebook renderers (open a notebook
with rendered output cells), Live Preview, etc. Less deterministic but
triggers the same code path.
Open code-server in Safari 18 on Mac or iPad. (Or any iOS browser —
Chrome/Brave/Firefox on iOS are all forced WebKit by App Store policy.)
Open Web Inspector → Console.
Click the extension's activity-bar icon to open its webview panel.
Observe blank panel +
TypeError: Underlying ArrayBuffer has been detached from the view or out-of-boundscascade in the console.Confirm it does NOT reproduce on Chromium: open the same code-server URL
in Chrome/Chromium/Brave/Edge on macOS/Linux/Windows. Same extension,
same workspace, panel renders normally.
Expected
The extension's webview panel renders normally — same as on Chromium
browsers, same as on native VS Code desktop.
Actual
The panel slot opens but stays permanently empty (no content, no error UI,
no spinner — just the empty letterpress background). The workbench console
fires a cascade of:
TypeError: Underlying ArrayBuffer has been detached from the view or out-of-bounds
Uint8Array.prototype.slice
VSBuffer.prototype.slice (out/vs/base/common/buffer.js)
ChunkStream._read (out/vs/base/parts/ipc/common/ipc.net.js)
ProtocolReader.onRawMessage / acceptChunk
…one per IPC chunk on the management socket, then one per chunk on the
extension-host socket. The cascade kills the message channel; any
subsequent IPC traffic for that extension is dead until reload.
ROOT CAUSE (short version — the structural fix is upstream in
microsoft/vscode, but the bug surfaces in practice via code-server because
code-server is browser-only):
VSBuffer.prototype.sliceinvs/base/common/buffer.tswas changed inmicrosoft/vscode#76076 from a copying
slice()to a non-copyingsubarray()for performance. That returns a Uint8Array VIEW into theunderlying ArrayBuffer, not a copy.
The view ends up queued inside ChunkStream while the IPC layer is reading
multi-chunk messages. Downstream, the extension-host iframe receives those
messages via
postMessage(msg, [transferList])— which DETACHES theunderlying ArrayBuffer. On V8 (Chromium/Node/Electron), subsequent
Uint8Array.prototype.slicecalls on the now-detached view are permissive(historically returned empty buffer; modern V8 throws but the detach
timing is loose enough that the cascade rarely fires). On JavaScriptCore
(Safari, all iOS browsers), the throw is immediate and unconditional per
TC39 ECMA-262 §25.1.1.7. The IPC reader unwinds, fires
handleUnexpectedError, and the channel is dead.Native VS Code (Electron) doesn't trip this because its IPC doesn't use
browser postMessage with transfer lists for VSBuffer payloads. That's why
the bug is invisible on desktop and only surfaces in browser-mode VS Code.
PROPOSED ACTION FOR code-server:
The minified bundle of
workbench.jsships verbatim from upstream andcontains the exact line
slice(i,e){return new a(this.buffer.subarray(i,e))}inside the VSBuffer class. Changing
subarraytoslice(single character)fixes the bug by forcing a copy — the new buffer is owned by no one else, so
nothing downstream can detach it.
I've been running this single-character patch in production for ~24h with
no observed regression. Suggested implementation: add a patch to
patches/(e.g.vsbuffer-slice-webkit.diff) that touchessrc/vs/base/common/buffer.ts:That fits the established pattern of other patches you carry against
upstream (
webview.diff,service-worker.diff,signature-verification.diff,etc.). Performance cost is negligible — one extra
memcpyperChunkStream chunk-split, sub-kilobyte typical message sizes.
I'm happy to open the PR if you'd like to carry this temporarily; or to
just keep this issue open as a downstream tracker until upstream merges
the structural fix in microsoft/vscode (which I'm filing separately).
Filing here, despite this being technically an upstream bug, because:
code-server users (every Safari/iPad/iOS user of any non-trivial
code-server deployment).
surfacing as different symptoms — Unable to load webview on iPad #4433, Dynamically webview resources fail to load in some browser/OS combinations #3410, etc. were filed and
closed over the years without the underlying mechanism being
identified.
performance optimization (#76076) and will likely need design
discussion before it lands, so a downstream patch unblocks affected
users in the meantime.
Logs
Screenshot/Video
No response
Does this bug reproduce in native VS Code?
No, this works as expected in native VS Code
Does this bug reproduce in VS Code web?
I did not test VS Code web
Does this bug reproduce in GitHub Codespaces?
Yes, this is also broken in GitHub Codespaces
Are you accessing code-server over a secure context?
Notes
No response