From 95551a425a34563ae6ffb4982ee2bc11250f60cf Mon Sep 17 00:00:00 2001 From: Ben Senescu <44480372+bensenescu@users.noreply.github.com> Date: Wed, 26 Aug 2026 11:39:50 -0400 Subject: [PATCH] chore: log expected credit refusals in backlink snapshot refresh at info (#541) --- .../dashboard/services/DashboardService.ts | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/server/features/dashboard/services/DashboardService.ts b/src/server/features/dashboard/services/DashboardService.ts index 2acff01..05ede7d 100644 --- a/src/server/features/dashboard/services/DashboardService.ts +++ b/src/server/features/dashboard/services/DashboardService.ts @@ -12,6 +12,8 @@ import { createDataforseoClient, normalizeBacklinksTarget, } from "@/server/lib/dataforseo"; +import { asAppError } from "@/server/lib/errors"; +import { shouldCaptureAppErrorCode } from "@/shared/error-codes"; // Daily cadence: fresh numbers each visit without per-visit spend; a dormant // project costs nothing because refreshes are visit-triggered. @@ -269,11 +271,24 @@ async function ensureBacklinkSnapshot(input: { capturedAt: new Date().toISOString(), }); } catch (error) { - if (latestMatchesDomain) { - console.error("dashboard: backlink snapshot refresh failed", error); - return getBacklinkSummary(projectId, domain); + if (!latestMatchesDomain) throw error; + // Visit-triggered refresh, so an out-of-credits org re-hits this on every + // dashboard load. Expected refusals are not failures: log them at info with + // the code, and keep error for anything the taxonomy says is reportable. + const code = asAppError(error)?.code; + if (shouldCaptureAppErrorCode(code)) { + console.error( + "dashboard: backlink snapshot refresh failed", + { projectId }, + error, + ); + } else { + console.info("dashboard: backlink snapshot refresh skipped", { + projectId, + code, + }); } - throw error; + return getBacklinkSummary(projectId, domain); } return getBacklinkSummary(projectId, domain);