Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/Rokt-Kit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,23 @@ function _getUserAgent(): string | undefined {
return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;
}

// Browser network-failure phrases (Chromium / WebKit / Firefox) raised when a request
// never produces a readable response — including a rate-limited 429 whose response is
// CORS-blocked or dropped. Reports describing such a failure must not be beaconed: doing
// so emits another request that is itself rate-limited/blocked, feeding the same limit.
const NETWORK_FAILURE_PATTERNS = [
'failed to fetch',
'load failed',
'networkerror when attempting to fetch resource',
'network request failed',
];

function _isNetworkFailureMessage(message: string | undefined): boolean {
if (!message) return false;
const normalized = message.toLowerCase();
return NETWORK_FAILURE_PATTERNS.some((pattern) => normalized.includes(pattern));
}

class RateLimiter {
private _logCount: Record<string, number> = {};

Expand Down Expand Up @@ -622,6 +639,12 @@ class ErrorReportingService {

report(error: ErrorReport | null | undefined): void {
if (!error) return;
// Drop reports that merely describe a transport/network failure of a prior request
// (e.g. an identity request that failed with "Failed to fetch", which is also how a
// rate-limited 429 surfaces). Beaconing these to /v1/errors emits another request
// that is itself rate-limited/blocked, feeding back into the same limit. Genuine
// application errors (different messages) are still reported.
if (_isNetworkFailureMessage(error.message)) return;
const severity = error.severity || WSDKErrorSeverity.ERROR;
this._transport.send(this._errorUrl, severity, error.message, error.code, error.stackTrace);
}
Expand Down
28 changes: 28 additions & 0 deletions test/src/tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6144,6 +6144,34 @@ describe('Rokt Forwarder', () => {
expect(body.reporter).toBe('mp-wsdk');
});

it.each([
'Error sending identity request to servers - Failed to fetch',
'Load failed',
'NetworkError when attempting to fetch resource.',
])('should NOT beacon a transport/network-failure report: "%s" (no feedback loop)', (message) => {
const service = new ErrorReportingServiceClass(
{ errorUrl: 'test.com/v1/errors', isLoggingEnabled: true },
'1.0.0',
'test-guid',
);
service.report({ message, code: ErrorCodesConst.UNKNOWN_ERROR, severity: WSDKErrorSeverityConst.ERROR });
expect(fetchCalls.length).toBe(0);
});

it('should still beacon a genuine application error (not a network failure)', () => {
const service = new ErrorReportingServiceClass(
{ errorUrl: 'test.com/v1/errors', isLoggingEnabled: true },
'1.0.0',
'test-guid',
);
service.report({
message: 'Error sending identity request to servers - e.split is not a function',
code: ErrorCodesConst.UNKNOWN_ERROR,
severity: WSDKErrorSeverityConst.ERROR,
});
expect(fetchCalls.length).toBe(1);
});

it('should send warning reports to the errors endpoint', () => {
const service = new ErrorReportingServiceClass(
{ errorUrl: 'test.com/v1/errors', isLoggingEnabled: true },
Expand Down