Skip to content

Fix docs chunk failures and error fallback#221

Open
dcrawbuck wants to merge 20 commits into
mainfrom
dcrawbuck/docs-indexing-debug
Open

Fix docs chunk failures and error fallback#221
dcrawbuck wants to merge 20 commits into
mainfrom
dcrawbuck/docs-indexing-debug

Conversation

@dcrawbuck

@dcrawbuck dcrawbuck commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • keep the Fumadocs server source graph out of the client docs route by loading it only inside the server loader
  • replace TanStack's default docs error UI with an index-safe docs error fallback
  • add retry-once handling for failed dynamic imports/preloads and noindex headers for error responses
  • prune stale changelog entries during generation instead of validating changelog links with client-side source
  • fix related PSI/hydration/a11y issues found while reproducing the indexing failure

Root 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 source graph 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 test passes: 55 tests
  • bun run lint passes with existing warnings only
  • bun run build:cf passes
  • local preview /docs/ios renders Welcome | Superwall Docs with no TanStack fallback text
  • local preview /docs/debug/error renders Error loading docs page, hides the raw error, and returns X-Robots-Tag: noindex, nofollow
  • built client output no longer contains fumadocs-mdx:collections/server, docs.toFumadocsSource, source.getPages, or related server-source signatures

Note

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/source only 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 imports source to filter URLs. Smaller fixes: Superchat iframe title/aria-label, MDX Card description wrapper for hydration, /debug/error for 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.

Comment thread scripts/generate-changelog.ts
Comment thread scripts/generate-changelog.ts
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 24, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

@dcrawbuck dcrawbuck marked this pull request as ready for review June 25, 2026 16:47

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +112 to +113
const existingEntries = pruneDeletedEntries(existing.entries);
const prunedExistingCount = existing.entries.length - existingEntries.length;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread src/components/DocsError.tsx Outdated
useEffect(() => {
document.title = ERROR_PAGE_TITLE;
upsertMeta("description", ERROR_PAGE_DESCRIPTION);
upsertMeta("robots", NOINDEX_ROBOTS_META);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 25, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
superwall-docs 0c9b6e6 Jun 30 2026, 07:26 PM

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/router.tsx Outdated
routeTree,
defaultPreload: "intent",
scrollRestoration: true,
disableGlobalCatchBoundary: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 })));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread scripts/generate-changelog.ts
Comment thread scripts/generate-changelog.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread src/routes/$.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/routes/__root.tsx Outdated
Comment on lines +67 to +69
} catch (_) {}

location.reload();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread src/routes/__root.tsx Outdated
Comment on lines +73 to +74
event.preventDefault();
reloadOnce();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@dcrawbuck dcrawbuck force-pushed the dcrawbuck/docs-indexing-debug branch from e64b944 to 0c9b6e6 Compare June 30, 2026 19:20
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.

1 participant