Skip to content

earlyErrorSuppression overrides console.error and window.onerror in production: real errors are silently dropped #831

Description

@nanaf6203-bit

earlyErrorSuppression overrides console.error and window.onerror in production: real errors are silently dropped

Labels / Complexity: Frontend · Medium Complexity — Medium

Problem

src/utils/earlyErrorSuppression.ts is imported first in src/app/layout.tsx and immediately replaces the browser's error surfaces:

console.error = (...args) => { if (shouldSuppress(...args)) return; originalConsoleError.apply(console, args); };
console.warn  = (...args) => { if (shouldSuppress(...args)) return; originalConsoleWarn.apply(console, args); };
window.addEventListener('error', handleError, true);          // preventDefault() on match
window.addEventListener('unhandledrejection', handleRejection); // preventDefault() on match
window.onerror = (message, source, lineno, colno, error) => { if (shouldSuppress(...)) return true; ... };

The process.env.NODE_ENV === 'development' check (line 21) only gates a console.log of the patterns — the suppression itself runs in all environments, including production. shouldSuppress is a case-insensitive substring match against wallet-extension IDs and script names (evmask.js, selectExtension, etc.) from src/config/wallets.ts. Any real error whose message happens to contain one of those substrings — a failed fetch from a wallet endpoint, an exception inside extension-adjacent code, an unhandled promise rejection mentioning an extension id — is swallowed: window.onerror returns true, unhandledrejection is preventDefault()-ed, and the error never reaches error-reporting/monitoring hooks. The module is deliberately imported as early as possible (src/app/layout.tsx), so this masking is active for the entire app lifecycle, and nothing in the module is environment-gated.

Root cause

src/utils/earlyErrorSuppression.ts: overrides installed unconditionally (the NODE_ENV check gates only the debug log), with substring-based shouldSuppress matching error text.

Why this is architecturally hard

  1. The feature exists for a real reason. Wallet extensions flood console.error with benign noise; a naive "disable in production" fix restores the noise the module was built to kill. The contributor must scope suppression precisely (e.g. only exact extension-id matches, only in dev, or only for the known extension scripts) without losing its purpose.
  2. Suppression and monitoring are coupled. Any fix must ensure real errors still reach the app's error-monitoring pipeline (src/utils/errorMonitoringService.ts); the interaction between preventDefault-style swallowing and the monitoring hooks is exactly what needs a test.

Acceptance criteria

  • In production, an error whose message contains a wallet-extension id still reaches the error-monitoring pipeline (test asserts it is not swallowed).
  • Benign wallet-extension noise is still suppressed where that behavior is kept — the tradeoff is documented.
  • The suppression scope is explicit (patterns and environments), with a test for each environment.
  • npm run lint and npm test pass.

Out of scope

Changing the wallet-extension pattern list contents.

Getting started

Files: src/utils/earlyErrorSuppression.ts, src/config/wallets.ts, src/app/layout.tsx. Commands: npm run lint, npm test. Good first files to read: src/utils/earlyErrorSuppression.ts, src/utils/__tests__/earlyErrorSuppression.test.ts.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions