diff --git a/packages/worker/client/sentry-browser-filters.node.test.ts b/packages/worker/client/sentry-browser-filters.node.test.ts index b6d25480c5..53244a4c20 100644 --- a/packages/worker/client/sentry-browser-filters.node.test.ts +++ b/packages/worker/client/sentry-browser-filters.node.test.ts @@ -4,6 +4,7 @@ import { filterBrowserInjectedGlobalNoiseSentryEvent, filterBrowserSentryEvent, filterChromeExtensionObjectNotFoundSentryEvent, + filterChromeExtensionReceivingEndMissingSentryEvent, filterFathomRemoveChildNullSentryEvent, filterFirefoxDomPermissionDeniedSentryEvent, filterMetaMaskExtensionSentryEvent, @@ -216,6 +217,59 @@ test('browser Sentry filters drop AbortError and Firefox Xray noise and keep rea }), ).toBeNull() + // Chrome extension receiving-end missing (issue 7662064169 / KODY-CLOUDFLARE-4F). + expect( + filterChromeExtensionReceivingEndMissingSentryEvent({ + exception: { + values: [ + { + type: 'Error', + value: + 'Could not establish connection. Receiving end does not exist.', + }, + ], + }, + }), + ).toBeNull() + expect( + filterChromeExtensionReceivingEndMissingSentryEvent( + { + exception: { + values: [{ type: 'Error', value: 'something else' }], + }, + }, + new Error( + 'Could not establish connection. Receiving end does not exist.', + ), + ), + ).toBeNull() + expect( + filterBrowserSentryEvent({ + exception: { + values: [ + { + type: 'Error', + value: + 'Error: Could not establish connection. Receiving end does not exist.', + }, + ], + }, + }), + ).toBeNull() + // Nearby connection wording from app code must not be dropped. + expect( + filterChromeExtensionReceivingEndMissingSentryEvent({ + exception: { + values: [ + { + type: 'Error', + value: 'Could not establish connection to the MCP server.', + }, + ], + }, + }), + ).not.toBeNull() + // MetaMask inpage.js session restore (issue 7658961865 / KODY-CLOUDFLARE-3X). expect( filterMetaMaskExtensionSentryEvent({ diff --git a/packages/worker/client/sentry-browser-filters.ts b/packages/worker/client/sentry-browser-filters.ts index 54b80aef85..be278d68af 100644 --- a/packages/worker/client/sentry-browser-filters.ts +++ b/packages/worker/client/sentry-browser-filters.ts @@ -323,6 +323,59 @@ export function filterChromeExtensionObjectNotFoundSentryEvent< return event } +/** + * Chrome/Firefox extension messaging noise: content scripts and page-injected + * extension code call `chrome.runtime.sendMessage` / `browser.runtime.sendMessage` + * when the extension's background/service worker (or another receiving end) is + * gone — unloaded, updated, or never registered for that tab. Chromium rejects + * with this exact IPC wording. The promise often surfaces on the host page with + * no app stack frames (Sentry attributes the culprit to the document URL). + * + * Signature from production issue 7662064169 / KODY-CLOUDFLARE-4F (Chrome on + * https://heykody.app/, zero frames, handled generic capture). Kody never uses + * `chrome.runtime` / `browser.runtime`. Match is intentionally narrow: only + * this exact "Could not establish connection. Receiving end does not exist" + * wording (optional `Error:` preface). Never blanket-drop connection errors. + */ +const chromeExtensionReceivingEndMissingMessage = + /^(?:Error:\s*)?Could not establish connection\. Receiving end does not exist\.?$/ + +export function isChromeExtensionReceivingEndMissingMessage(message: string) { + return chromeExtensionReceivingEndMissingMessage.test(message.trim()) +} + +export function isChromeExtensionReceivingEndMissingError(error: unknown) { + if (typeof error === 'string') { + return isChromeExtensionReceivingEndMissingMessage(error) + } + if (typeof error !== 'object' || error === null) return false + if (!('message' in error) || typeof error.message !== 'string') return false + return isChromeExtensionReceivingEndMissingMessage(error.message) +} + +export function isChromeExtensionReceivingEndMissingSentryEvent( + event: SentryErrorEventLike, + originalException?: unknown, +) { + if (isChromeExtensionReceivingEndMissingError(originalException)) return true + return sentryEventMessages(event).some( + (message) => + typeof message === 'string' && + isChromeExtensionReceivingEndMissingMessage(message), + ) +} + +export function filterChromeExtensionReceivingEndMissingSentryEvent< + T extends SentryErrorEventLike, +>(event: T, originalException?: unknown): T | null { + if ( + isChromeExtensionReceivingEndMissingSentryEvent(event, originalException) + ) { + return null + } + return event +} + /** * MetaMask (`chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/…`) injects * `inpage.js` into every page and tries to restore a wallet session on load. @@ -536,6 +589,14 @@ export function filterBrowserSentryEvent( ) { return null } + if ( + filterChromeExtensionReceivingEndMissingSentryEvent( + event, + originalException, + ) === null + ) { + return null + } if (filterMetaMaskExtensionSentryEvent(event, originalException) === null) { return null } diff --git a/packages/worker/client/sentry-init.ts b/packages/worker/client/sentry-init.ts index 4b4893ad49..bd3ac621c4 100644 --- a/packages/worker/client/sentry-init.ts +++ b/packages/worker/client/sentry-init.ts @@ -32,14 +32,15 @@ export function initBrowserSentry(config: SentryClientConfig) { // permission-denied from Replay/hydration on restricted nodes, // injected wallet/`__firefox__` globals, Fathom beacon // removeChild-on-null, Chrome extension IPC "Object Not Found - // Matching Id…", MetaMask inpage connect failures, Twitter/X - // in-app browser chrome `CONFIG` ReferenceErrors, and injected - // unguarded `meta[property='og:type']` probes from `global code`) — - // see filterBrowserSentryEvent / KODY-CLOUDFLARE-23 / + // Matching Id…", Chrome/Firefox extension "Receiving end does not + // exist", MetaMask inpage connect failures, Twitter/X in-app browser + // chrome `CONFIG` ReferenceErrors, and injected unguarded + // `meta[property='og:type']` probes from `global code`) — see + // filterBrowserSentryEvent / KODY-CLOUDFLARE-23 / // KODY-CLOUDFLARE-3Q / KODY-CLOUDFLARE-3S / KODY-CLOUDFLARE-3X / - // KODY-CLOUDFLARE-43 / KODY-CLOUDFLARE-46 / issues 7639685398, - // 7648833360, 7648833403, 7653117289, 7655189301, 7658961865, - // 7659616372, 7660258027. + // KODY-CLOUDFLARE-43 / KODY-CLOUDFLARE-46 / KODY-CLOUDFLARE-4F / + // issues 7639685398, 7648833360, 7648833403, 7653117289, 7655189301, + // 7658961865, 7659616372, 7660258027, 7662064169. beforeSend(event, hint) { return filterBrowserSentryEvent(event, hint.originalException) },