Reduce noisy JavaScript errors reported to Rollbar - #3625
Merged
Conversation
Filter out unactionable client errors that reach Rollbar but reflect no fault in our code: - ResizeObserver loop notifications — a benign, deferred-to-next-frame browser signal with no user impact. - Every error originating from the Monaco editor bundle we load from the jsdelivr CDN (worker importScripts failures, AMD `define` collisions with browser-extension scripts, CDN/adblock load failures), via a new `isMonacoCdnError` helper matching the versionless CDN path in either the message or any stack frame. To keep the user-visible signal we'd otherwise lose, wire the code editor's ErrorBoundary to report a distinct `Code editor failed to render` message — fired only when the editor subtree actually crashes in render — kept separate from the CDN noise so it is not self-filtered. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generalize two more classes of unactionable client errors so future variants do not resurface as new Rollbar items: - Scripts injected into the page by the visitor's environment (userscripts, in-app browsers) that reference globals only defined in that environment. `isInjectedScriptError` keys on where the script lives — a `user-script:*` origin, or a ReferenceError from an inline script whose origin is the page document URL — rather than the specific missing global, so a new in-app browser with a different global name is covered without a config change. - WalletConnect/AppKit rejecting a pending pairing when its TTL elapses before the user completes the connect flow (proposal / pairing / session request expired) — expected user behaviour surfaced from vendored SDK code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A non-compliant wallet extension can dispatch `eip6963:announceProvider` with a null or partial `detail`; dereferencing `detail.info.rdns` threw an uncaught "Cannot read properties of null (reading 'info')". Bail when `detail.info` is absent, matching the optional chaining already used for `detail.provider` on the next line. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The null-detail announce events dispatched by non-compliant wallet extensions are read unguarded not only by our own detection hook but by wagmi's vendored `mipd` store (`providerDetail.info.uuid`), which throws the same uncaught "Cannot read properties of null (reading 'info')" and which we cannot patch. Install a capture-phase `eip6963:announceProvider` listener before wagmi config creates the mipd store. window listeners for a window-targeted event fire in registration order, so ours runs first and `stopImmediatePropagation()`s any event whose `detail.info` is missing, neutralising it for mipd and every other consumer at once — a single guard for the whole class rather than a per-consumer null check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "Maximum call stack size exceeded" items originate from an injected inline script (a DOM walker recursing over large list pages), attributed to a document URL on our own origin rather than a `/_next/` bundle — the same class as the injected-global ReferenceErrors already filtered, just a RangeError. Generalize `isInjectedScriptError`: match both ReferenceError and RangeError from an inline document script, and key on same-origin rather than an exact URL match so a client-side navigation (origin frame in the document the script was parsed in, request URL on the current route) is still recognised. Guard against matching our own bundle by excluding `/_next/` assets and `.js` files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The _error.tsx instance had global capture on and no checkIgnore, so it re-reported, unfiltered, noise the browser instance already drops (e.g. the AbortError in #390, captured there as an uncaught trace). Turn its global capture off and give it a minimal, self-contained config. Share only the window-free exception-class check (isIgnoredExceptionClass) between the two instances — the browser predicate's bot / headless checks read window, which is absent on the server, and keeping the shared surface narrow stops a future browser-only rule from crashing the server render. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auto-translate and DOM-mutating extensions move nodes React owns, so React's commit-phase removeChild / insertBefore / replaceChild fail with a NotFoundError. checkIgnore already drops the NotFoundError *class*, but our error boundary re-reports it as a message (no body.trace), which the class check can't see — so 3000+ of these leaked past it. Match the shared DOM-exception tail via ignoredMessages, which reads the message body. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The early window 'error' listener forwarded every uncaught error to Rollbar, so a public page's browser extensions, injected wallet SDKs, userscript inline scripts, CDN assets, and opaque cross-origin "Script error." events all became noise we can't act on. Rollbar has no positive allowlist for "only my code", so gate at the listener: forward only when the browser attributes the error to a script under /_next/. Genuine page crashes are unaffected — they report through AppErrorBoundary as explicit rollbar.critical calls, not this listener. Third-party packages bundled into our chunks still come through (in a prod build they carry no source path to distinguish them); those stay on the mute track. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that the early listener forwards only errors originating in our own /_next/ bundle, the rules that existed to drop foreign-origin uncaught noise never fire — those errors are dropped before they reach Rollbar: - isInjectedScriptError: every case it caught (user-script:*, injected inline on the document URL — all non-/_next/) is now gated at the listener. Removed it and its now-unused helper getExceptionOriginFileName. - IGNORED_ORIGIN_FILE_NAMES_CHUNKS: chrome-extension:// is gated; the /node_modules/@WalletConnect|@reown paths only ever match after server-side source-mapping, so never matched client-side in a prod build anyway. - ignoredMessages 'Script error' and 'ResizeObserver loop': both arrive as empty-filename window errors, which the origin gate drops. Kept isMonacoCdnError (own-bundle traces that reach the CDN deeper in the stack still pass the gate) and the bot / headless / chunk / exception-class / message rules (they apply to explicit rollbar calls too). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tom2drum
commented
Aug 10, 2026
tom2drum
left a comment
Collaborator
Author
There was a problem hiding this comment.
| Reviewed | d082c2cc → 2af19527 |
| Round | 1 of 3 |
| Findings | 1 blocker · 3 major · 3 nit |
| By axis | Spec 1 · Standards 4 · Correctness 2 |
| Outcome | blocked |
Not anchorable
- nit · Spec — PR Additional information says AppKit/wagmi items were muted in the Rollbar dashboard rather than given code rules, but
clientConfigstill adds'Proposal expired' | 'Pairing expired' | 'Session request expired'. Update the PR text to own these, or drop the code rules. - pre-existing (out of scope) —
isIgnoredExceptionClassdrops everyNotFoundError/AbortErrorby class alone (same breadth as before this PR). Narrowing to the DOM-exception message is a possible follow-up, not introduced here.
Two of this change's own filters were silently discarding the reports they were meant to preserve: - CodeEditor reported editor render crashes by passing the Error instance, so Rollbar built a stack trace whose frames run through the Monaco CDN bundle — which isMonacoCdnError then dropped. Report the error as message data instead. - _error.tsx reported only err.message, a payload with no exception class, so the shared isIgnoredExceptionClass check never fired. Pass the Error as a throwable so a trace with body.trace.exception.class exists; capture server-side uncaught exceptions, which carry no third-party window noise. Also: rename installEip6963AnnounceGuard to kebab-case and make it a named export; annotate isMonacoCdnError's return type; drop the Dictionary coercions from the utils spec (plain literals satisfy the index-signature type). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`buildServerConfig` is instantiated at module scope in `_error.tsx`, which also loads in the browser on a client-side error-page navigation. With `captureUncaught` unconditionally on, that client-side instance installs a `window.onerror` handler that bypasses the queue's own-bundle origin gate and recaptures the third-party noise this change set out to drop. Enable it only on the server, where those page-crash errors surface nowhere else. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tom2drum
commented
Aug 11, 2026
tom2drum
left a comment
Collaborator
Author
There was a problem hiding this comment.
| Reviewed | 2af19527 → e77233567 (arbitration of Round 1) |
| Round | 2 of 3 |
| Findings | 0 blocker · 0 major · 1 nit |
| By axis | Spec 1 · Standards 0 · Correctness 0 |
| Outcome | clear |
Arbitration
| Finding | Ruling |
|---|---|
| F1 blocker · CodeEditor Error-as-throwable | fixed-accepted — plain { cause, stack } data; isMonacoCdnError cannot drop it |
F2 major · server checkIgnore / _error shape |
fixed-accepted — rollbar.error(err ?? …) builds exception.class; captureUncaught server-gated as documented |
| F3 major · default export | fixed-accepted — named export |
| F4 major · camelCase filename | fixed-accepted — install-eip6963-announce-guard.ts |
F5 nit · isMonacoCdnError return type |
fixed-accepted — : boolean |
F6 nit · as unknown as Dictionary |
fixed-accepted — casts removed |
No fix-introduced regressions. The server captureUncaught: typeof window === 'undefined' expansion is intentional (JSDoc why-comment) and out of bounds to re-litigate.
Not anchorable
- nit · Spec (still open from R1) — PR Additional information still says AppKit/wagmi items were muted in the dashboard rather than given code rules, while
clientConfigkeeps'Proposal expired' | 'Pairing expired' | 'Session request expired'. Update the PR text to own these, or drop the code rules.
tom2drum
commented
Aug 11, 2026
tom2drum
left a comment
Collaborator
Author
There was a problem hiding this comment.
| Reviewed | 2af19527 → e77233567 (arbitration correction) |
| Round | 2 of 3 |
| Findings | 0 blocker · 0 major · 0 nit |
| By axis | Spec 0 · Standards 0 · Correctness 0 |
| Outcome | clear |
Correction to the prior Round 2 body: the remaining Spec nit is closed. The PR description now owns the WalletConnect expiry ignoredMessages ("Expected WalletConnect/AppKit connect-flow expiries… are dropped via ignoredMessages code rules") and separately describes dashboard mutes for low-count items — so that R1 not-anchorable finding no longer holds.
All six Round 1 findings remain fixed-accepted. No open findings.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Reduces noise in our Rollbar JavaScript error monitoring, where a large share of items came from third-party scripts, browser extensions, and abandoned wallet flows that never reach real users, drowning out real signal.
errorlistener forwarded every uncaught error to Rollbar, so a public page's browser extensions, injected wallet SDKs, userscript inline scripts, CDN assets, and opaque cross-originScript error.events all became noise. Rollbar has no positive "only my code" allowlist, so we gate at the listener: forward an uncaught error only when the browser attributes it to a script under/_next/. Genuine page crashes are unaffected — they report throughAppErrorBoundaryas explicitrollbar.criticalcalls, not this listener._error.tsx) reused none of the browser instance's filtering. It now shares the window-free exception-class check and reports the page-crash error as a throwable so that check can read its exception class. It captures server-side uncaught exceptions — which carry none of the third-partywindownoise the browser instance fights — while leaving unhandled-rejection capture off.removeChild/insertBeforefails with aNotFoundError(3000+ occurrences). Matched generically via the shared DOM-exception message tail.Script error/ResizeObserver loop), a net −165 lines.Also includes two genuine fixes surfaced during triage: guarding the EIP-6963
announceProviderhandler against a nulldetail(plus a capture-phase guard so wagmi's bundledmipdstore is covered too), and reporting code-editor render-boundary crashes explicitly instead of relying on the silenced Monaco CDN noise.Environment variables
None.
Minimum API version
None.
Breaking or incompatible changes
None. Note this changes what client errors are captured: after deploy, several currently-active items dominated by third-party scripts (notably
Script error.) should stop arriving.Additional information
Filtering here suppresses at ingestion. Expected WalletConnect/AppKit connect-flow expiries (proposal / pairing / session request) are dropped via
ignoredMessagescode rules — they recur and match generically. Separately, a handful of low-count, vendored/environmental crash items (wagmi/AppKit, helia, phoenix) were muted directly in the Rollbar dashboard rather than given code rules, since a dependency bump may retire them.