Stop reporting GA4 quota exhaustion as an app exception (#527)

This commit is contained in:
Ben Senescu 2026-08-26 09:40:47 -04:00 committed by GitHub
parent b592dc5043
commit ce7570e898
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 14 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import { z } from "zod";
import { shiftGa4Date } from "@/server/features/ga4/services/Ga4Dates"; import { shiftGa4Date } from "@/server/features/ga4/services/Ga4Dates";
import { Ga4OrganicOverviewService } from "@/server/features/ga4/services/Ga4OrganicOverviewService"; import { Ga4OrganicOverviewService } from "@/server/features/ga4/services/Ga4OrganicOverviewService";
import { Ga4Service } from "@/server/features/ga4/services/Ga4Service"; import { Ga4Service } from "@/server/features/ga4/services/Ga4Service";
import { AppError } from "@/server/lib/errors";
import { Ga4ReportError } from "@/server/lib/ga4Errors"; import { Ga4ReportError } from "@/server/lib/ga4Errors";
import { hasSelfHostedGoogleOAuthConfig } from "@/server/features/google/oauth-config"; import { hasSelfHostedGoogleOAuthConfig } from "@/server/features/google/oauth-config";
import { import {
@ -123,6 +124,15 @@ export const getGa4DashboardReport = createServerFn({ method: "POST" })
) { ) {
return { connected: false as const }; return { connected: false as const };
} }
// Google's per-property reporting quota is exhausted: an external,
// transient condition, not an app fault. Surface it as RATE_LIMITED so
// error tracking skips it — the card keeps its own "try again" copy.
if (
error instanceof Ga4ReportError &&
error.code === "ga4_quota_exhausted"
) {
throw new AppError("RATE_LIMITED");
}
throw error; throw error;
} }
}); });

View File

@ -10,6 +10,7 @@ describe("shouldCaptureAppErrorCode", () => {
"AUDIT_CAPACITY_REACHED", "AUDIT_CAPACITY_REACHED",
"AUDIT_PAGE_LIMIT_EXCEEDED", "AUDIT_PAGE_LIMIT_EXCEEDED",
"AUDIT_ALREADY_RUNNING", "AUDIT_ALREADY_RUNNING",
"RATE_LIMITED",
] as const)("skips expected %s errors", (code) => { ] as const)("skips expected %s errors", (code) => {
expect(shouldCaptureAppErrorCode(code)).toBe(false); expect(shouldCaptureAppErrorCode(code)).toBe(false);
}); });

View File

@ -27,6 +27,9 @@ export type ErrorCode = z.infer<typeof errorCodeSchema>;
const NON_REPORTABLE_ERROR_CODES = new Set<ErrorCode>([ const NON_REPORTABLE_ERROR_CODES = new Set<ErrorCode>([
"UNAUTHENTICATED", "UNAUTHENTICATED",
// External throttling (Google Analytics quota, Autumn, DataForSEO): expected
// and transient, and nothing in the app can act on it.
"RATE_LIMITED",
"NOT_FOUND", "NOT_FOUND",
"PAYMENT_REQUIRED", "PAYMENT_REQUIRED",
"INSUFFICIENT_CREDITS", "INSUFFICIENT_CREDITS",