From 66c13d3b9717086380943a1e46b292405e7bd1d3 Mon Sep 17 00:00:00 2001 From: Darren Li Date: Tue, 23 Jun 2026 15:53:08 +1000 Subject: [PATCH] fix(logging): stop reporting failed /v1/log sends as error beacons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LoggingService.log() reported a failed /v1/log POST to the error endpoint (/v1/errors). When /v1/log is rate-limited the 429 surfaces to fetch as a "Failed to fetch" TypeError, so each dropped log emits an error beacon that is itself rate-limited — a self-amplifying feedback loop at the log frequency. Make diagnostics-log sends fire-and-forget. ReportingTransport already logs the failure to the console; genuine errors still flow through ErrorReportingService. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Rokt-Kit.ts | 22 ++++++---------------- test/src/tests.spec.ts | 10 +++++----- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 5f91127..470774d 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -647,22 +647,12 @@ class LoggingService { log(entry: LogEntry | null | undefined): void { if (!entry) return; - this._transport.send( - this._loggingUrl, - WSDKErrorSeverity.INFO, - entry.message, - entry.code, - undefined, - (error: Error) => { - if (this._errorReportingService) { - this._errorReportingService.report({ - message: 'LoggingService: Failed to send log: ' + error.message, - code: ErrorCodes.UNKNOWN_ERROR, - severity: WSDKErrorSeverity.ERROR, - }); - } - }, - ); + // Fire-and-forget: a failed diagnostics-log send must NOT be re-reported as an + // error beacon. Doing so creates a feedback loop — when /v1/log is rate-limited, + // the 429 surfaces to fetch as "Failed to fetch", and reporting it would emit a + // /v1/errors beacon that is itself rate-limited, amplifying load at the log + // frequency. ReportingTransport already logs the failure to the console. + this._transport.send(this._loggingUrl, WSDKErrorSeverity.INFO, entry.message, entry.code); } } diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 8fc54b8..1ba7785 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -6338,14 +6338,14 @@ describe('Rokt Forwarder', () => { expect(body.additionalInformation.message).toBe('log entry'); }); - it('should report failure through ErrorReportingService on fetch error', async () => { + it('should NOT report failure as an error beacon on fetch error (fire-and-forget, no feedback loop)', async () => { const errorReports: any[] = []; const errorService = { report: (error: any) => { errorReports.push(error); }, }; - (window as any).fetch = () => Promise.reject(new Error('Network failure')); + (window as any).fetch = () => Promise.reject(new Error('Failed to fetch')); const originalConsoleError = console.error; console.error = () => {}; @@ -6353,9 +6353,9 @@ describe('Rokt Forwarder', () => { service.log({ message: 'test' }); await new Promise((resolve) => setTimeout(resolve, 50)); - expect(errorReports.length).toBeGreaterThan(0); - expect(errorReports[0].severity).toBe('ERROR'); - expect(errorReports[0].message).toContain('Failed to send log'); + // A failed /v1/log send must not generate a /v1/errors beacon — otherwise a + // rate-limited log send amplifies into rate-limited error traffic. + expect(errorReports.length).toBe(0); console.error = originalConsoleError; });