From c80734d366046dbf31ff9830c87f024bdb631ab5 Mon Sep 17 00:00:00 2001 From: Anton Pascal Date: Wed, 1 Jul 2026 04:07:17 +0000 Subject: [PATCH] fix(viewer): require obtainable WebGL context before mounting 3D viewer (MONOREPO-EDITOR-59) Guard the /viewer/:id route against the null-GL-context crash: TypeError: Cannot read properties of null (reading 'getSupportedExtensions') (Sentry MONOREPO-EDITOR-59, 5282 events / 0 users = headless bots & GPU-blocklisted browsers). canMountGpuViewer() previously mounted whenever 'gpu' in navigator was truthy. That flag does not guarantee WebGPU can produce a device: on headless/blocklisted browsers the adapter request fails and three.js falls back to a WebGL backend. With no obtainable WebGL context, three calls getSupportedExtensions() on the null context inside renderer.init() and throws. Since the WebGPURenderer fallback path always needs WebGL, an obtainable WebGL context is the real precondition for mounting. Require it unconditionally so bots hit the existing UnsupportedGpuViewerFallback instead of crashing. Real WebGPU-capable browsers always expose WebGL, so no capable device is newly excluded. --- .../viewer/src/components/viewer/index.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/viewer/src/components/viewer/index.tsx b/packages/viewer/src/components/viewer/index.tsx index 497f36e8a..1d4603cd3 100644 --- a/packages/viewer/src/components/viewer/index.tsx +++ b/packages/viewer/src/components/viewer/index.tsx @@ -93,9 +93,26 @@ function canCreateWebGLContext() { } } +// Sentry MONOREPO-EDITOR-59: `TypeError: Cannot read properties of null +// (reading 'getSupportedExtensions')` — 5282 events / 0 users, i.e. headless +// bots and GPU-blocklisted browsers crawling the public /viewer/:id route. +// +// The old check treated `'gpu' in navigator` as sufficient to mount. But that +// flag being present does NOT guarantee WebGPU can produce a device: on +// headless/blocklisted browsers the adapter request fails and three.js +// transparently falls back to a WebGL backend. If a real WebGL context can't be +// created either, three calls `gl.getSupportedExtensions()` on the null context +// deep inside `renderer.init()` and throws. +// +// Because the WebGPURenderer's fallback path always needs WebGL, an obtainable +// WebGL context is the true, verifiable precondition for mounting. Requiring it +// unconditionally turns the flaky "gpu-in-navigator" optimism into a hard gate +// that headless bots fail cleanly — they get the fallback UI instead of the +// null-context crash. Real browsers with WebGPU always expose WebGL too, so no +// capable device is newly excluded. function canMountGpuViewer() { if (typeof window === 'undefined') return false - if (!('gpu' in navigator) && !canCreateWebGLContext()) return false + if (!canCreateWebGLContext()) return false return true }