Add global error listeners for uncaught errors on special pages#2442
Add global error listeners for uncaught errors on special pages#2442noisysocks wants to merge 2 commits intomainfrom
Conversation
Register window 'error' and 'unhandledrejection' listeners on onboarding, new-tab, and history pages to catch JS errors that fall outside Preact's ErrorBoundary (event handlers, async callbacks, timers, observers, etc.). Caught errors are reported to native via reportInitException with [uncaught] or [unhandledrejection] prefixes. Also adds integration tests for each page verifying the listeners catch errors and produce no false positives during normal operation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Build Branch
Static preview entry points
QR codes (mobile preview)
Integration commandsnpm (Android / Extension): Swift Package Manager (Apple): .package(url: "https://github.com/duckduckgo/content-scope-scripts.git", branch: "pr-releases/worktree-error-reporting")git submodule (Windows): git -C submodules/content-scope-scripts fetch origin pr-releases/worktree-error-reporting
git -C submodules/content-scope-scripts checkout origin/pr-releases/worktree-error-reportingPin to exact commitnpm (Android / Extension): Swift Package Manager (Apple): .package(url: "https://github.com/duckduckgo/content-scope-scripts.git", revision: "406cdf632cbb81fd823cd2feea060122d4dccdaa")git submodule (Windows): git -C submodules/content-scope-scripts fetch origin pr-releases/worktree-error-reporting
git -C submodules/content-scope-scripts checkout 406cdf632cbb81fd823cd2feea060122d4dccdaa |
[Beta] Generated file diffTime updated: Thu, 12 Mar 2026 10:09:51 GMT Apple
File has changed Integration
File has changed Windows
File has changed |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Web Compatibility Assessment
No compatibility regressions found in this diff.
special-pages/pages/history/src/index.js(line range 86-94), severityinfo: Globalerror/unhandledrejectionlisteners are registered beforeinit(), and payload shape sent toreportInitException({ message })matches existing API contract.special-pages/pages/new-tab/src/index.js(line range 113-121), severityinfo: Listener behavior and contract (reportInitException(string)) are consistent with the existing New Tab wrapper method.special-pages/pages/onboarding/app/index.js(line range 40-48), severityinfo: Listener behavior and contract (reportInitException({ message })) are consistent with onboarding messaging.special-pages/pages/*/integration-tests/*(new global listener suites), severityinfo: Added positive and false-positive coverage for uncaught errors/unhandled rejections; this reduces rollback risk.
Security Assessment
No security vulnerabilities identified in the changed code.
special-pages/pages/history/src/index.js(line range 86-94), severityinfo: Outgoing notify payload is bounded to{ message: string }; nonativeDatapropagation path introduced.special-pages/pages/new-tab/src/index.js(line range 113-121), severityinfo: No trust-boundary relaxation, no new cross-origin messaging, no dynamic code execution.special-pages/pages/onboarding/app/index.js(line range 40-48), severityinfo: Listener-only instrumentation; no new external fetch/postMessage/eval surface.
Risk Level
Medium Risk — runtime behavior changed in three special-page entrypoints by adding global listeners that emit new native notifications, but changes are localized and covered by integration tests.
Recommendations
- Add a robustness test for
unhandledrejectionwhereevent.reasonis a non-Errorobject with a throwing coercion path (e.g., customtoString/Symbol.toPrimitive), and harden message extraction with localtry/catch. - Consider lightweight de-duplication/rate limiting for repeated identical global errors to avoid noisy native telemetry if a timer-driven fault loops.
- Keep the onboarding mock default for
reportInitException(already added) to prevent test-environment feedback loops when notification handlers are absent.
Validation run: npm run test-int -- --grep "global error listeners" --reporter list (12 passed).
Sent by Cursor Automation: Web compat and sec
|
This PR requires a manual review and approval from a member of one of the following teams:
|


Asana Task/Github Issue: https://app.asana.com/1/137249556945/project/1209121419454298/task/1213433496794376?focus=true
Description
Registers
window.addEventListener('error')andwindow.addEventListener('unhandledrejection')listeners on three special pages — onboarding, new-tab, and history — to catch uncaught JavaScript errors that fall outside Preact's ErrorBoundary (event handlers, async callbacks, timers, observers, etc.). Caught errors are reported to native viareportInitExceptionwith[uncaught]or[unhandledrejection]prefixes so native can distinguish these from actual init failures.Key details:
init()is calledreportInitExceptiontakes a bare string (matching its existing API); onboarding and history take{ message }reportPageException— no double-reportingreportInitException: {}to onboarding's mock default responses to prevent infinite error loops on WindowsManual verification
Deliberate
throw new Error(...)statements were inserted into real Preact callbacks across all three pages, rebuilt withnpm run build.dev, and tested in a headless Chromium browser to confirm the global listeners catch errors and callreportInitException.What the global listeners catch
SearchForm.input()— onInput handleruseButtonClickHandler— document click listenerWelcomeContent.complete()— setTimeout inside useEffectsetTimeout(() => { throw ... })Promise.reject(new Error(...))In every case,
reportInitExceptionwas called (confirmed via mock transport logging "unhandled notification") and the page continued to render normally — errors did not break the UI.What they don't catch (expected)
ThemeSectiononClickPlusIconWrapperonClickPreact catches errors thrown inside
onClickhandlers via its internal error recovery (options.__e) and does not propagate them towindow.onerror. These errors would instead be caught by Preact'sErrorBoundaryand reported viareportPageException— so no gap in coverage exists, just a different reporting path.False positive check
All three pages were loaded and interacted with normally. Zero
reportInitExceptioncalls were observed during regular operation (confirmed by checking console output for "unhandled notification" messages).Testing Steps
npm run test-int -- --grep "global error listeners"Checklist
🤖 Generated with Claude Code