Fix docs chunk failures and error fallback#221
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
superwall-docs-staging | aba5551 | Commit Preview URL Branch Preview URL |
Jun 25 2026, 12:21 AM |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aba5551588
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const existingEntries = pruneDeletedEntries(existing.entries); | ||
| const prunedExistingCount = existing.entries.length - existingEntries.length; |
There was a problem hiding this comment.
Persist stale changelog pruning without new entries
When pruneDeletedEntries removes existing entries here, a delete-only docs change (or any run where the only stale entries are for deleted files) can still leave newChanges.length === 0, so the early return below exits without calling writeChangelogIfChanged. Since ChangelogTimeline no longer validates URLs against source.getPages(), those stale deleted-page entries remain in changelog-entries.json and can render broken changelog links until an unrelated new entry is generated.
Useful? React with 👍 / 👎.
| useEffect(() => { | ||
| document.title = ERROR_PAGE_TITLE; | ||
| upsertMeta("description", ERROR_PAGE_DESCRIPTION); | ||
| upsertMeta("robots", NOINDEX_ROBOTS_META); |
There was a problem hiding this comment.
Remove noindex meta when leaving error fallback
When the docs error UI is shown during client-side navigation and the user later returns to a valid docs page without a full reload (for example via browser Back after the route error), this manually inserted robots tag is outside TanStack's head management and no normal docs route removes it. The valid page can therefore keep noindex, nofollow in the DOM after the error component unmounts, which works against the SEO recovery this fallback is meant to provide.
Useful? React with 👍 / 👎.
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
superwall-docs | 0c9b6e6 | Jun 30 2026, 07:26 PM |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bb3ad9359f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| routeTree, | ||
| defaultPreload: "intent", | ||
| scrollRestoration: true, | ||
| disableGlobalCatchBoundary: true, |
There was a problem hiding this comment.
Keep the global catch boundary enabled
With this option enabled, errors that escape a route-local boundary—especially a route chunk/dynamic import failure before the docs route's own boundary has loaded—bubble to the browser instead of rendering defaultErrorComponent or RootErrorComponent. That leaves the stale-chunk path this change is trying to make index-safe with a top-level failure once the reload handler declines to reload again, so the custom DocsError fallback is not guaranteed to be shown.
Useful? React with 👍 / 👎.
|
|
||
| const getIsLocalDebugHost = createServerFn({ | ||
| method: "GET", | ||
| }).handler(() => isLocalDebugHost(getRequestHost({ xForwardedHost: true }))); |
There was a problem hiding this comment.
Gate the debug error route on dev builds
Because this check trusts X-Forwarded-Host, a production request to /docs/debug/error that supplies X-Forwarded-Host: localhost can pass the local-host guard and intentionally throw a 500 instead of returning notFound(). Since this route is only for exercising the fallback locally, gate it on the build environment rather than a forwarded request header.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes 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 b73bff1. Configure here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b73bff1b82
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } catch (_) {} | ||
|
|
||
| location.reload(); |
There was a problem hiding this comment.
Skip reload when the marker cannot be stored
When sessionStorage is unavailable or setItem throws (for example storage is disabled or full), this catch still falls through to location.reload() without persisting the marker. If the chunk remains unavailable after refresh, the next page load takes the same path and reloads indefinitely for those clients, so the one-shot guard fails; bail out or use a fallback marker before reloading.
Useful? React with 👍 / 👎.
| event.preventDefault(); | ||
| reloadOnce(); |
There was a problem hiding this comment.
Let repeat chunk failures reach the error boundary
After one reload has already been recorded for the current URL, a second vite:preloadError within the TTL still calls preventDefault() before reloadOnce() returns early. In that persistent-failure case (for example a still-missing CDN chunk or blocked chunk), Vite will not throw the import error and this handler also does not reload, so the user can be left on the old page without the new DocsError fallback; only suppress the event when a reload will actually happen, or explicitly surface the fallback when the guard declines.
Useful? React with 👍 / 👎.
e64b944 to
0c9b6e6
Compare

Summary
sourcegraph out of the client docs route by loading it only inside the server loadersourceRoot Cause
Google/PageSpeed was able to hit a docs page state where a large generated docs JS chunk failed to load. Because the catch-all docs route imported the Fumadocs server
sourcegraph at module scope, the client route bundle could include far more docs corpus code than needed. A failed chunk then surfaced through TanStack's default fallback text:Something went wrong! Show Error, which Google indexed across affected docs pages.Validation
bun testpasses: 55 testsbun run lintpasses with existing warnings onlybun run build:cfpasses/docs/iosrendersWelcome | Superwall Docswith no TanStack fallback text/docs/debug/errorrendersError loading docs page, hides the raw error, and returnsX-Robots-Tag: noindex, nofollowfumadocs-mdx:collections/server,docs.toFumadocsSource,source.getPages, or related server-source signaturesNote
Medium Risk
Touches core docs routing, SEO/error handling, and client bundle boundaries; behavior is well-tested but affects every docs page load and error path.
Overview
Fixes docs pages that crawlers indexed as TanStack’s “Something went wrong!” fallback when large JS chunks failed to load.
The catch-all docs route now dynamically imports
@/lib/sourceonly inside the server loader so the Fumadocs server graph is not pulled into the client bundle. DocsError replaces the default error UI (no raw error text), with noindex metadata,X-Robots-Tag, and root/docs error headers. A one-time reload script handles Vite chunk/preload failures.Changelog generation prunes entries whose MDX files are gone (
sanitizeEntries/writeChangelogIfChanged); ChangelogTimeline no longer importssourceto filter URLs. Smaller fixes: Superchat iframe title/aria-label, MDX Card description wrapper for hydration,/debug/errorfor local error testing, and unit tests for error metadata and DocsError.Reviewed by Cursor Bugbot for commit 0c9b6e6. Bugbot is set up for automated code reviews on this repo. Configure here.