fix(viewer): guard 3D viewer against null WebGL context (Sentry MONOREPO-EDITOR-59)#455
fix(viewer): guard 3D viewer against null WebGL context (Sentry MONOREPO-EDITOR-59)#455anton-pascal wants to merge 3 commits into
Conversation
|
Nightly Sentry triage (2026-07-11) — EDITOR-59 recurred with a new latest event (issue 7415398488) but this time from transaction Heads up that the guard in this PR touches |
|
Nightly Sentry triage 2026-07-13: MONOREPO-EDITOR-59 ( |
|
Sentry MONOREPO-EDITOR-FM ( |
|
Nightly Sentry triage (2026-07-17): MONOREPO-EDITOR-FM ( |
|
Nightly Sentry triage (2026-07-18): MONOREPO-EDITOR-FM ( |
|
Triage result: the null-context diagnosis is credible, but this one-predicate patch is not complete enough to merge. The follow-up Sentry events in this thread show the same crash on /editor/:projectId, while this branch only changes the Viewer mount gate. Also, requiring WebGL unconditionally assumes every usable WebGPU environment exposes WebGL; the robust contract is to centralize renderer capability/initialization and cover every canvas entry point, with the existing unsupported-GPU fallback handling init failure. Please rebase onto current main and update this as a shared gate/fallback with regression coverage for: no navigator.gpu + no WebGL, navigator.gpu present but adapter/device unavailable, and renderer initialization failure. I’m keeping it open because the production signal is still active, but the current diff is blocked on that scope. |
c80734d to
81b1661
Compare
|
Reworked and rebased this onto current
Fresh verification after the rebase:
@Aymericr re-requesting review on the centralized implementation. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ff7c81f. Configure here.
| }) | ||
|
|
||
| return Promise.race([promise, timeoutPromise]).finally(() => clearTimeout(timeout)) | ||
| } |
There was a problem hiding this comment.
Timed-out GPU work left running
Medium Severity
withTimeout only races promises; it does not abort requestAdapter / requestDevice / init. A late-resolved GPUDevice is never destroyed, so timed-out capability checks can leave orphan devices that count against the browser’s tight concurrent-device limit.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit ff7c81f. Configure here.
|
Nightly Sentry triage 2026-07-21: this error class is still occurring on latest release 4bcc2e2 as a new issue ID — MONOREPO-EDITOR-FM |


Sentry MONOREPO-EDITOR-59
TypeError: Cannot read properties of null (reading 'getSupportedExtensions')— 5282 events / 0 users.The 0-users signature is the tell: this is headless bot / crawler traffic (plus GPU-blocklisted or hardware-acceleration-disabled browsers) hitting the public
/viewer/:idroute.Root cause
Stack: three.js
three.webgpu.js→Backend/Rendererinit→ WebGPU is unavailable so three.js transparently falls back to a WebGL backend →canvas.getContext('webgl2' | 'webgl')returnsnull→ three callsgl.getSupportedExtensions()on the null context and throws.The viewer already has a capability gate (
canMountGpuViewer, added in #403) plus anUnsupportedGpuViewerFallback. But the gate mounted the canvas whenever'gpu' in navigatorwas truthy:That
navigator.gpuflag being present does not guarantee WebGPU can produce a device. On headless/blocklisted browsers the adapter request fails, three falls back to WebGL, and if no real WebGL context is obtainable either,renderer.init()throws on the null context — the crash above. So the "optimistic" branch let exactly the crashing population through.The fix
Require an actually obtainable WebGL context before mounting, unconditionally:
Rationale: the
WebGPURendererfallback path always needs WebGL, so a creatable WebGL context is the true, verifiable precondition for mounting. This converts the flaky "gpu-in-navigator" optimism into a hard gate:UnsupportedGpuViewerFallbackUI instead of crashing.Minimal, low-risk: one predicate changed, reusing the existing
canCreateWebGLContext()helper and the existing fallback UI. No renderer/init refactor.Scope
packages/viewer/src/components/viewer/index.tsx(the/viewerroute renderer gate).Do not merge without review.
Note
Medium Risk
Changes how and when the 3D canvas mounts and how WebGPU/WebGL backends are selected; mistakes could affect real browsers or leave headless traffic in a hung init state, though behavior is covered by new tests.
Overview
Adds
renderer-capability(detectRendererCapability,initializeGpuRenderer) so the viewer no longer relies on a pre-mountnavigator.gpu/ WebGL probe gate. The R3Fglfactory now runs through this helper: request a WebGPU device (with a 4s timeout), probe WebGL on a separate canvas, createWebGPURendererwith{ device }or{ forceWebGL: true }, and on WebGPUinitfailure dispose and retry on WebGL before surfacingunsupported.ViewerdropscanMountViewer/ inline capability helpers;showGpuFallbackis driven only byrendererInitFailedafter init fails.UnsupportedGpuViewerFallbackmoves to its own module (copy tweak). The capability APIs are re-exported from@pascal-app/viewer, with Bun tests covering timeouts, WebGL fallback, probe isolation, and the null-context failure path tied to Sentry MONOREPO-EDITOR-59.Reviewed by Cursor Bugbot for commit ff7c81f. Bugbot is set up for automated code reviews on this repo. Configure here.