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
- 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.
- 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.
earlyErrorSuppression overrides console.error and window.onerror in production: real errors are silently dropped
Labels / Complexity: Frontend · Medium Complexity — Medium
Problem
src/utils/earlyErrorSuppression.tsis imported first insrc/app/layout.tsxand immediately replaces the browser's error surfaces:The
process.env.NODE_ENV === 'development'check (line 21) only gates aconsole.logof the patterns — the suppression itself runs in all environments, including production.shouldSuppressis a case-insensitive substring match against wallet-extension IDs and script names (evmask.js,selectExtension, etc.) fromsrc/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.onerrorreturnstrue,unhandledrejectionispreventDefault()-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 (theNODE_ENVcheck gates only the debug log), with substring-basedshouldSuppressmatching error text.Why this is architecturally hard
src/utils/errorMonitoringService.ts); the interaction betweenpreventDefault-style swallowing and the monitoring hooks is exactly what needs a test.Acceptance criteria
npm run lintandnpm testpass.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.