From 613ca5cfca6d224e8f79d55acd6064c58badebd7 Mon Sep 17 00:00:00 2001 From: dwebxr Date: Tue, 18 Aug 2026 16:03:28 +0900 Subject: [PATCH] =?UTF-8?q?fix(telemetry):=20DOM=20Event=20=E6=8B=92?= =?UTF-8?q?=E5=90=A6=E3=81=AE=20Sentry=20=E3=83=8E=E3=82=A4=E3=82=BA?= =?UTF-8?q?=E3=82=92=E6=97=A2=E5=AD=98=20non-Error=20=E6=97=8F=E3=81=A8?= =?UTF-8?q?=E5=90=8C=E6=A7=98=E3=81=AB=20drop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2026-08-18 mainnet 実観測 (ID 4441ed4b…): 存在しない /en/faq ([handle] catch-all の 404) 上で、DOM Event (type=error) を理由とする unhandled rejection が 「Event `Event` (type=error) captured as promise rejection」として捕捉された。 既存フィルタは「Non-Error promise rejection captured…」の文言だけを見ており、 拒否理由が DOM Event のときの別文言をすり抜けていた。 - 同族 (script/リソース読込失敗の promise 化・ウォレット拡張由来・スタックなし・ 非アクショナブル) なので /captured as promise rejection/ を ignoreErrors へ追加 - 自前コードは Error 以外で reject しないため実シグナルの取りこぼしなし - フェンス: 両文言が drop され自前 Error 文言は drop されないことをテストで固定 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_015CNMEwurCJNTrzvr2qypSd --- instrumentation-client.ts | 7 +++++++ tests/instrumentation-client.test.ts | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/instrumentation-client.ts b/instrumentation-client.ts index 3644fbe0..7e74b642 100644 --- a/instrumentation-client.ts +++ b/instrumentation-client.ts @@ -37,6 +37,13 @@ if (dsn) { // 投げる (throw new Error(...)) ため、実シグナルの取りこぼしリスクは実質ゼロ。 ignoreErrors: [ /Non-Error promise rejection captured/, + // 同族の兄弟パターン: 拒否理由が DOM Event のとき Sentry は上とは別の文言 + // 「Event `Event` (type=error) captured as promise rejection」を生成し、上の + // フィルタをすり抜ける (2026-08-18 mainnet 実観測: 存在しない /en/faq への + // 旧 UA Chrome 123 アクセスの 404 上・スタックなし・非アクショナブル)。 + // script/リソース読込失敗の promise 化やウォレット拡張が典型で、自前コードは + // Error 以外で reject しないため実シグナルは落ちない。 + /captured as promise rejection/, // ウォレット拡張/SDK の接続・再接続失敗 (MetaMask 等)。ページ読込時の自動再接続 // (wagmi reconnectOnMount=true) や、ロック中/権限未付与/接続キャンセルのウォレットで // 日常的に発生する。スタックはウォレット拡張内 (chrome-extension://…/inpage.js) を指し diff --git a/tests/instrumentation-client.test.ts b/tests/instrumentation-client.test.ts index cb05e0f4..30b9da6f 100644 --- a/tests/instrumentation-client.test.ts +++ b/tests/instrumentation-client.test.ts @@ -15,6 +15,25 @@ describe('instrumentation-client telemetry hooks', () => { sentry.replayIntegration.mockClear(); }); + it('ignoreErrors が non-Error 拒否の両文言 (value 形式 / DOM Event 形式) を落とす', async () => { + await import('@/instrumentation-client'); + const options = sentry.init.mock.calls[0][0] as { + ignoreErrors: RegExp[]; + }; + const matches = (msg: string) => + options.ignoreErrors.some((re) => re.test(msg)); + // 既存: 値付きの non-Error 拒否 + expect( + matches('Non-Error promise rejection captured with value: undefined'), + ).toBe(true); + // 2026-08-18 実観測の兄弟パターン: DOM Event が拒否理由のときの別文言 + expect( + matches('Event `Event` (type=error) captured as promise rejection'), + ).toBe(true); + // 実シグナル (自前 Error) は落とさない + expect(matches('Error: relay settle failed')).toBe(false); + }); + it('beforeBreadcrumb / beforeSendTransaction に URL scrubber を設定する', async () => { await import('@/instrumentation-client');