From fd613c4c648eb79f3ceac616e935e090d87df6da Mon Sep 17 00:00:00 2001 From: coji Date: Wed, 25 Mar 2026 22:11:29 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20Sentry=20=E3=81=AE=E3=83=9C=E3=83=83?= =?UTF-8?q?=E3=83=88/=E3=82=B9=E3=82=AD=E3=83=A3=E3=83=8A=E3=83=BC?= =?UTF-8?q?=E3=83=8E=E3=82=A4=E3=82=BA=E3=82=92=E3=83=95=E3=82=A3=E3=83=AB?= =?UTF-8?q?=E3=82=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - beforeSend で react-router internal エラー (404/400) を Sentry に送信しない - handleError で Error オブジェクトの internal 404/400 も早期フィルタ - webhook ルートに loader を追加し GET リクエストに 405 を返す Fixes UPFLOW-3, UPFLOW-4, UPFLOW-7 Co-Authored-By: Claude Opus 4.6 (1M context) --- app/entry.server.tsx | 10 +++++++++- app/routes/api.github.webhook.ts | 4 ++++ instrument.server.mjs | 13 +++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/app/entry.server.tsx b/app/entry.server.tsx index 1bd0de74..29ede6a9 100644 --- a/app/entry.server.tsx +++ b/app/entry.server.tsx @@ -13,8 +13,16 @@ const sentryHandleError = Sentry.createSentryHandleError({ }) export const handleError: typeof sentryHandleError = (error, ctx) => { - // Skip reporting 404s to Sentry (bot/scanner noise) + // Skip react-router internal errors (bot/scanner 404s, missing-loader 400s) if (error instanceof Response && error.status === 404) return + if ( + error instanceof Error && + 'status' in error && + 'internal' in error && + ((error as { status: number }).status === 404 || + (error as { status: number }).status === 400) + ) + return sentryHandleError(error, ctx) } diff --git a/app/routes/api.github.webhook.ts b/app/routes/api.github.webhook.ts index b6395c49..42a85c5a 100644 --- a/app/routes/api.github.webhook.ts +++ b/app/routes/api.github.webhook.ts @@ -2,6 +2,10 @@ import { verifyWebhookSignature } from '~/app/libs/webhook-verify.server' import { processGithubWebhookPayload } from '~/app/services/github-webhook.server' import type { Route } from './+types/api.github.webhook' +export const loader = () => { + return new Response('Method Not Allowed', { status: 405 }) +} + export const action = async ({ request }: Route.ActionArgs) => { if (request.method !== 'POST') { return new Response('Method Not Allowed', { status: 405 }) diff --git a/instrument.server.mjs b/instrument.server.mjs index 9aa3ccf2..7270e06a 100644 --- a/instrument.server.mjs +++ b/instrument.server.mjs @@ -13,5 +13,18 @@ if (dsn) { dsn, sendDefaultPii: true, tracesSampleRate: parseSampleRate(process.env.SENTRY_TRACES_SAMPLE_RATE), + beforeSend(event) { + // Filter out react-router internal errors (404 bot/scanner noise, 400 missing loader) + const serialized = event.extra?.__serialized__ + if ( + serialized && + typeof serialized === 'object' && + serialized.internal === true && + (serialized.status === 404 || serialized.status === 400) + ) { + return null + } + return event + }, }) }