From f75a1d4156cfd572814a78c5fa80fb32cbb162a9 Mon Sep 17 00:00:00 2001 From: Ben Senescu <44480372+bensenescu@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:23:49 -0400 Subject: [PATCH] fix: stop reporting handled payment required errors (#70) * fix: stop reporting handled payment required errors * save --- src/client/components/DefaultCatchBoundary.tsx | 13 ++----------- src/middleware/errorHandling.ts | 11 ++--------- src/shared/error-codes.test.ts | 18 ++++++++++++++++++ src/shared/error-codes.ts | 13 +++++++++++++ 4 files changed, 35 insertions(+), 20 deletions(-) create mode 100644 src/shared/error-codes.test.ts diff --git a/src/client/components/DefaultCatchBoundary.tsx b/src/client/components/DefaultCatchBoundary.tsx index b0afd9a..c69af04 100644 --- a/src/client/components/DefaultCatchBoundary.tsx +++ b/src/client/components/DefaultCatchBoundary.tsx @@ -1,6 +1,7 @@ import { Link, rootRouteId, useMatch, useRouter } from "@tanstack/react-router"; import type { ErrorComponentProps } from "@tanstack/react-router"; import * as React from "react"; +import { shouldCaptureAppErrorCode } from "@/shared/error-codes"; import { getErrorCode, getStandardErrorMessage, @@ -9,16 +10,6 @@ import { AuthConfigErrorCard } from "@/client/components/AuthConfigErrorCard"; import { captureClientError } from "@/client/lib/posthog"; import { UnauthenticatedErrorCard } from "@/client/components/UnauthenticatedErrorCard"; -function shouldCaptureBoundaryError( - errorCode: ReturnType, -) { - return ( - errorCode !== "UNAUTHENTICATED" && - errorCode !== "NOT_FOUND" && - errorCode !== "VALIDATION_ERROR" - ); -} - export function DefaultCatchBoundary({ error }: ErrorComponentProps) { const router = useRouter(); const isRoot = useMatch({ @@ -34,7 +25,7 @@ export function DefaultCatchBoundary({ error }: ErrorComponentProps) { const errorCode = getErrorCode(error); React.useEffect(() => { - if (!shouldCaptureBoundaryError(errorCode)) { + if (!shouldCaptureAppErrorCode(errorCode)) { return; } diff --git a/src/middleware/errorHandling.ts b/src/middleware/errorHandling.ts index cff99e8..189d29e 100644 --- a/src/middleware/errorHandling.ts +++ b/src/middleware/errorHandling.ts @@ -1,17 +1,10 @@ import { createMiddleware } from "@tanstack/react-start"; import { getRequest } from "@tanstack/react-start/server"; import { waitUntil } from "cloudflare:workers"; +import { shouldCaptureAppErrorCode } from "@/shared/error-codes"; import { asAppError, toClientError } from "@/server/lib/errors"; import { captureServerError } from "@/server/lib/posthog"; -function shouldCaptureServerError(code: string | null | undefined) { - return ( - code !== "UNAUTHENTICATED" && - code !== "NOT_FOUND" && - code !== "VALIDATION_ERROR" - ); -} - export const errorHandlingMiddleware = createMiddleware({ type: "function", }).server(async (c) => { @@ -26,7 +19,7 @@ export const errorHandlingMiddleware = createMiddleware({ const appError = asAppError(error); - if (shouldCaptureServerError(appError?.code)) { + if (shouldCaptureAppErrorCode(appError?.code)) { const request = getRequest(); const url = new URL(request.url); diff --git a/src/shared/error-codes.test.ts b/src/shared/error-codes.test.ts new file mode 100644 index 0000000..3758fa8 --- /dev/null +++ b/src/shared/error-codes.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from "vitest"; +import { shouldCaptureAppErrorCode } from "@/shared/error-codes"; + +describe("shouldCaptureAppErrorCode", () => { + it.each([ + "UNAUTHENTICATED", + "NOT_FOUND", + "PAYMENT_REQUIRED", + "VALIDATION_ERROR", + ] as const)("skips expected %s errors", (code) => { + expect(shouldCaptureAppErrorCode(code)).toBe(false); + }); + + it("captures unexpected errors and unknown failures", () => { + expect(shouldCaptureAppErrorCode("INTERNAL_ERROR")).toBe(true); + expect(shouldCaptureAppErrorCode(undefined)).toBe(true); + }); +}); diff --git a/src/shared/error-codes.ts b/src/shared/error-codes.ts index 54e4195..4e700e2 100644 --- a/src/shared/error-codes.ts +++ b/src/shared/error-codes.ts @@ -20,6 +20,19 @@ export const errorCodeSchema = z.enum(ERROR_CODES); export type ErrorCode = z.infer; +const NON_REPORTABLE_ERROR_CODES = new Set([ + "UNAUTHENTICATED", + "NOT_FOUND", + "PAYMENT_REQUIRED", + "VALIDATION_ERROR", +]); + export function isErrorCode(value: string): value is ErrorCode { return errorCodeSchema.safeParse(value).success; } + +export function shouldCaptureAppErrorCode( + code: ErrorCode | null | undefined, +): boolean { + return code == null || !NON_REPORTABLE_ERROR_CODES.has(code); +}