Skip to content

chore(frontend): log chunk preload failures to Datadog - #9847

Open
kevin9foong wants to merge 1 commit into
developfrom
chore/log-vite-preload-error
Open

chore(frontend): log chunk preload failures to Datadog#9847
kevin9foong wants to merge 1 commit into
developfrom
chore/log-vite-preload-error

Conversation

@kevin9foong

@kevin9foong kevin9foong commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Problem

A lazy route chunk that fails to load leaves the app blank, and we have no telemetry for it today — so we cannot tell whether it is actually happening, how often, or to whom.

What this does

Registers a vite:preloadError listener that logs to Datadog and does nothing else.

Deliberately observe-only:

  • It does not call preventDefault(), so Vite still rethrows and existing error reporting is unchanged.
  • No reload, no retry. Recovery is a separate decision, taken once the logs say whether it is warranted.

Logged fields: pathname, isPublicForm, app version, and the error message/stack.

isPublicForm as any future recovery would have to treat the two differently (a reload would discard a half-filled form).

Notes for the reviewer

  • registerChunkPreloadErrorLogger returns an unregister function. The app never calls it — the handler lives as long as the page — but tests need it so listeners do not accumulate on the shared jsdom window.
  • The public form pathname regex mirrors the backend's /:formId([a-fA-F0-9]{24}) in frontend.routes.ts.

Tests

apps/frontend/src/app/chunkPreloadError.test.ts — 8 tests, passing. Covers the pathname matcher, the logged payload, the public form flag, that the rethrow is not suppressed, and that unregistering stops handling.

@kevin9foong
kevin9foong requested review from a team and a lite review from Copilot August 13, 2026 05:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds client-side telemetry for Vite lazy-route chunk preload failures by registering a vite:preloadError listener and reporting the error context to Datadog (observe-only; no recovery behavior).

Changes:

  • Register registerChunkPreloadErrorLogger() early in the app bootstrap.
  • Add a new chunkPreloadError module to detect “public form” routes and log preload failures to Datadog.
  • Add unit tests covering pathname classification, logging payload, non-suppression, and unregister behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
apps/frontend/src/index.tsx Registers the preload error logger before React render so early lazy imports are observed.
apps/frontend/src/app/chunkPreloadError.ts Implements the Vite vite:preloadError event handler + public-form pathname detection + Datadog logging.
apps/frontend/src/app/chunkPreloadError.test.ts Adds Vitest coverage for route matching, logging payload, and listener lifecycle.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +46 to +57
datadogLogs.logger.error('Chunk preload failed', {
meta: {
action: 'chunk-preload-failed',
pathname: window.location.pathname,
version: import.meta.env.VITE_APP_VERSION,
isPublicForm: isPublicFormPathname(window.location.pathname),
error: {
message: event.payload?.message,
stack: event.payload?.stack,
},
},
})
Comment on lines +18 to +23
const setPathname = (pathname: string) => {
Object.defineProperty(window, 'location', {
configurable: true,
value: { pathname },
})
}
Comment thread apps/frontend/src/index.tsx Outdated
Comment on lines +18 to +19
registerChunkPreloadErrorLogger()

A lazy route chunk that fails to load leaves the app blank, and we have no
telemetry for it today, so we cannot tell whether it happens at all.

Two causes are plausible. A tab left open across a deploy requests a hash
that is no longer on the container's disk — already mitigated by
catchNonExistentStaticRoutesMiddleware falling back to the S3 static
assets bucket. Or Cloudflare issues a cf-mitigated challenge for the chunk
request itself: the interstitial comes back as HTML, the browser rejects
the module, and nothing catches it, because we only handle challenges for
API calls in the ApiService interceptor and a dynamic import() is a plain
browser fetch that axios never sees.

This observes only. The handler does not call preventDefault(), so Vite
still rethrows and existing error reporting is unchanged. Recovery — a
reload is the only client-side option for the challenge case — waits until
the logs say whether it is warranted, and how often.

The pathname is logged as isPublicForm because a respondent-facing failure
is more severe than an admin one, and any future recovery would have to
treat the two differently.

The registration returns an unregister function. The app never calls it —
the handler lives as long as the page — but without it every test in the
suite leaks a listener onto the shared jsdom window.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@kevin9foong
kevin9foong force-pushed the chore/log-vite-preload-error branch from 74d7301 to 84e87e6 Compare August 13, 2026 06:10
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.

2 participants