diff --git a/packages/browser/src/transports/fetch.ts b/packages/browser/src/transports/fetch.ts index 4c7cc3230fde..5f64bcdde841 100644 --- a/packages/browser/src/transports/fetch.ts +++ b/packages/browser/src/transports/fetch.ts @@ -1,8 +1,9 @@ import { createTransport } from '@sentry/core'; import { Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; +import { rejectedSyncPromise } from '@sentry/utils'; import { BrowserTransportOptions } from './types'; -import { FetchImpl, getNativeFetchImplementation } from './utils'; +import { clearCachedFetchImplementation, FetchImpl, getNativeFetchImplementation } from './utils'; /** * Creates a Transport that uses the Fetch API to send events to Sentry. @@ -30,13 +31,18 @@ export function makeFetchTransport( ...options.fetchOptions, }; - return nativeFetch(options.url, requestOptions).then(response => ({ - statusCode: response.status, - headers: { - 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), - 'retry-after': response.headers.get('Retry-After'), - }, - })); + try { + return nativeFetch(options.url, requestOptions).then(response => ({ + statusCode: response.status, + headers: { + 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), + 'retry-after': response.headers.get('Retry-After'), + }, + })); + } catch (e) { + clearCachedFetchImplementation(); + return rejectedSyncPromise(e); + } } return createTransport(options, makeRequest); diff --git a/packages/browser/src/transports/utils.ts b/packages/browser/src/transports/utils.ts index 137cbc88487e..1243c2cc4292 100644 --- a/packages/browser/src/transports/utils.ts +++ b/packages/browser/src/transports/utils.ts @@ -1,6 +1,6 @@ import { isNativeFetch, logger, WINDOW } from '@sentry/utils'; -let cachedFetchImpl: FetchImpl; +let cachedFetchImpl: FetchImpl | undefined = undefined; export type FetchImpl = typeof fetch; @@ -76,3 +76,8 @@ export function getNativeFetchImplementation(): FetchImpl { return (cachedFetchImpl = fetchImpl.bind(WINDOW)); /* eslint-enable @typescript-eslint/unbound-method */ } + +/** Clears cached fetch impl */ +export function clearCachedFetchImplementation(): void { + cachedFetchImpl = undefined; +} diff --git a/packages/browser/test/unit/transports/fetch.test.ts b/packages/browser/test/unit/transports/fetch.test.ts index 6bf6671ea1da..624a3d1d007a 100644 --- a/packages/browser/test/unit/transports/fetch.test.ts +++ b/packages/browser/test/unit/transports/fetch.test.ts @@ -98,4 +98,13 @@ describe('NewFetchTransport', () => { ...REQUEST_OPTIONS, }); }); + + it('handles when `getNativeFetchImplementation` is undefined', async () => { + const mockFetch = jest.fn(() => undefined) as unknown as FetchImpl; + const transport = makeFetchTransport(DEFAULT_FETCH_TRANSPORT_OPTIONS, mockFetch); + + expect(mockFetch).toHaveBeenCalledTimes(0); + await expect(() => transport.send(ERROR_ENVELOPE)).not.toThrow(); + expect(mockFetch).toHaveBeenCalledTimes(1); + }); });