From 25c8f7be26a32c69a675d348007760206d6719a8 Mon Sep 17 00:00:00 2001 From: Ben Senescu <44480372+bensenescu@users.noreply.github.com> Date: Wed, 26 Aug 2026 09:48:43 -0400 Subject: [PATCH] fix(client): quick fixes for top PostHog errors (#436) --- src/client/lib/posthog.ts | 7 +++++++ src/router.tsx | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/client/lib/posthog.ts b/src/client/lib/posthog.ts index 5298361..5adb43e 100644 --- a/src/client/lib/posthog.ts +++ b/src/client/lib/posthog.ts @@ -40,6 +40,13 @@ function isIgnorableException( if (value.includes("Object Not Found Matching Id")) return true; if (value === "Script error.") return true; if (value.includes("signal is aborted without reason")) return true; + // ResizeObserver's loop warning is benign by spec (the browser defers + // delivery to the next frame) but surfaces as an error. Caveat: this also + // mutes a real ResizeObserver->setState feedback loop, if we ship one. + if (value.includes("ResizeObserver loop")) return true; + // The PostHog SDK's own network timeouts — capturing them just makes error + // tracking report on itself. + if (value.includes("PostHog request timed out")) return true; if (value === "CancelledError") return true; const frames = entry?.stacktrace?.frames; return ( diff --git a/src/router.tsx b/src/router.tsx index 60581ba..a6726af 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -3,6 +3,36 @@ import { routeTree } from "./routeTree.gen"; import { DefaultCatchBoundary } from "./client/components/DefaultCatchBoundary"; import { NotFound } from "./client/components/NotFound"; +const PRELOAD_ERROR_RELOAD_KEY = "vite-preload-error-reloaded-at"; + +// After a deploy replaces the hashed chunks, dynamic imports in already-open +// tabs 404 ("Importing a module script failed." / "Unexpected token '<'"). +// Reload to pick up the new build, at most once per 30s so a failure a reload +// can't fix surfaces as an error instead of looping. Registered at module +// scope, not in a component, so it also catches chunks that fail while +// hydration is still in progress. Vite only emits this event in builds — +// exercise it via `vite preview` or a deploy, never `vite dev`. +if (typeof window !== "undefined") { + window.addEventListener("vite:preloadError", (event) => { + try { + const reloadedAt = Number( + window.sessionStorage.getItem(PRELOAD_ERROR_RELOAD_KEY) ?? 0, + ); + if (Date.now() - reloadedAt < 30_000) return; + window.sessionStorage.setItem( + PRELOAD_ERROR_RELOAD_KEY, + String(Date.now()), + ); + } catch { + // Without sessionStorage we can't bound reload attempts, so let the + // error surface instead of risking a reload loop. + return; + } + event.preventDefault(); + window.location.reload(); + }); +} + export function getRouter() { const router = createTanStackRouter({ routeTree,