Skip to content

fix: recover from chunk/page-data load failures instead of blank screen#18125

Draft
posthog[bot] wants to merge 2 commits into
masterfrom
posthog-code/docs-nav-error-boundary
Draft

fix: recover from chunk/page-data load failures instead of blank screen#18125
posthog[bot] wants to merge 2 commits into
masterfrom
posthog-code/docs-nav-error-boundary

Conversation

@posthog

@posthog posthog Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Changes

Visitors browsing docs/handbook/templates were hitting blank or half-rendered screens during client-side navigation. This adds graceful recovery for the two failure modes behind that symptom, neither of which the app previously handled at all.

Root cause (investigated): the app had no error boundary and no retry anywhere in the tree. During client-side navigation Gatsby fetches JS chunks and page-data.json; when those fail — overwhelmingly a stale-deploy race where the loaded HTML references asset hashes that no longer exist on the CDN — the navigation dies and the visitor is left on a blank screen (ChunkLoadError, We couldn't load "/page-data/…json"). Uncaught render exceptions had the same effect.

Note on the original hypothesis (RenderInClient SSR/client hydration mismatch from the flag-gating commit): investigation did not support it. RenderInClient returns null on both the server and the first client render, so it is already hydration-consistent, and the React #418 / chunk / page-data signals were all first seen 7+ months before that commit and fire site-wide, not only on flag-gated pages. React #418 is also a recoverable hydration warning (React re-renders) rather than the permanent-blank cause. So this PR targets the genuinely-blocking mechanism instead.

What changed:

  • src/components/ErrorBoundary (new): catches render-time exceptions. Stale-asset errors trigger a guarded one-time reload (max once per 15s via sessionStorage, so a truly-missing asset can't loop) which re-fetches the fresh post-deploy assets; any other error renders a small "This page didn't load correctly" fallback with a Reload page button instead of a blank screen. Resets on route change.
  • src/components/AppWindow/index.tsx: wraps the page Router output in the boundary.
  • gatsby-browser.tsx: registers global unhandledrejection / error listeners in onClientEntry that recover from stale-asset failures. These are thrown inside Gatsby's loader during navigation, outside React render, so an error boundary alone can't catch them — this is what covers the highest-volume page-data / chunk failures.
  • src/components/RadixUI/Select.tsx: defensive guard so undefined menu items can't crash the product switcher (the visibleChildren?.map(...)Select path in ReaderView).

Add screenshots or screen recordings for visual / UI-focused changes.

Verification note: these failures are production deploy-timing races that can't be reproduced with pnpm start (dev serves assets on demand), so the reload recovery can't be observed locally. The symptom and mechanism were confirmed against error-tracking data — thousands of real sessions hitting ChunkLoadError and page-data load failures on exactly these docs/handbook/tutorials URLs — and the change is type-clean (tsc --noEmit). The render-exception fallback path is locally verifiable by throwing from a page component.

Checklist

  • I've read the docs and/or content style guides.
  • Words are spelled using American English
  • Use relative URLs for internal links
  • I've checked the pages added or changed in the Vercel preview build
  • If I moved a page, I added a redirect in vercel.json

Created with PostHog Code from an inbox report.

Client-side navigation on posthog.com had no error boundary and no retry
for Gatsby chunk / page-data fetch failures, so a failed fetch (typically a
stale-deploy asset-hash race) or an uncaught render exception left visitors
on a blank screen — most visibly clicking through the docs/handbook sidebar.

- Add `src/components/ErrorBoundary`: guarded one-time reload for stale-asset
  errors (chunk / page-data load failures), and a visible "reload page"
  fallback for other render errors.
- Wrap the page `Router` output in `AppWindow` with the boundary.
- Register global `unhandledrejection` / `error` listeners in
  `gatsby-browser.tsx` `onClientEntry` — Gatsby loader fetch failures happen
  outside React render, so a boundary alone can't catch them.
- Harden the RadixUI `Select` against `undefined` items.

Generated-By: PostHog Code
Task-Id: c2177caf-e750-4c49-893c-4ecc8096ab2a
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Deploy preview

Status Details Updated (UTC)
🟢 Ready View preview Jul 02, 2026 11:00AM

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Bundle report

Total JS (gzip)

6.29 MiB (+0.8 KiB / +0.0%)

Eager graph (static-import closure per entrypoint)

Entrypoint Eager size Budget Modules
app 24.22 MiB (+5.6 KiB / +0.0%) report-only 5516
Largest modules in the app closure
Module Size
css ./node_modules/.pnpm/css-loader@5.2.7_webpack@5.101.3/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[8].oneOf[1].use[1]!./node_modules/.pnpm/postcss-loader@4.3.0_postcss@8.5.6_webpack@5.101.3/node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[8].oneOf[1].use[2]!./src/styles/global.css 708.0 KiB
./src/components/Stickers/Stickers.tsx 696.4 KiB
./.cache/caches/gatsby-plugin-mdx/mdx-scopes-dir/31a094f140f119e73085d847ae81b99b.js + 2 modules 579.3 KiB
./node_modules/.pnpm/@radix-ui+react-icons@1.3.2_react@18.3.1/node_modules/@radix-ui/react-icons/dist/react-icons.esm.js 481.4 KiB
./node_modules/.pnpm/@codemirror+view@6.38.2/node_modules/@codemirror/view/dist/index.js 458.1 KiB
./node_modules/.pnpm/rehype-raw@7.0.0/node_modules/rehype-raw/lib/index.js + 29 modules 395.1 KiB
./node_modules/.pnpm/@posthog+icons@0.36.6_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.cjs.js 364.8 KiB
./node_modules/.pnpm/@posthog+icons@0.36.6_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js 354.8 KiB
./src/hooks/useCustomers.tsx + 54 modules 353.9 KiB
./node_modules/.pnpm/react-markdown@8.0.7_@types+react@16.14.66_react@18.3.1/node_modules/react-markdown/lib/react-markdown.js + 88 modules 351.4 KiB
./node_modules/.pnpm/cloudinary-core@2.14.0_lodash@4.17.21/node_modules/cloudinary-core/cloudinary-core.js 281.9 KiB
./src/components/ProductComparisonTable/index.tsx + 116 modules 267.6 KiB
./node_modules/.pnpm/@codesandbox+sandpack-react@2.20.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@codesandbox/sandpack-react/dist/index.mjs 266.6 KiB
./node_modules/.pnpm/d3@7.9.0/node_modules/d3/src/index.js + 208 modules 247.4 KiB
./src/components/Pricing/PricingSlider/Slider.tsx + 87 modules 239.9 KiB

Eager-graph budgets are report-only until a baseline is established. Sizes are gzip of public/**/*.js; eager size is webpack module source bytes.

Sentence-case the heading, capitalize PostHog, and use en dashes to satisfy
the repo's Vale prose rules.

Generated-By: PostHog Code
Task-Id: c2177caf-e750-4c49-893c-4ecc8096ab2a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants