Commit 67b58968 authored by Vignesh Mohankumar's avatar Vignesh Mohankumar Committed by GitHub

fix: ignore ChunkLoadingErrors if related to 499 or missing data (#6365)

* fix: ignore ChunkLoadingErrors if related to 499 or missing data

* add tests

* more tests

* types

* no syntaxerror

* handle undefined performance

* change test name

* comment

* Update src/tracing/errors.ts
Co-authored-by: default avatarZach Pomerantz <zzmp@uniswap.org>

* Update src/tracing/errors.test.ts
Co-authored-by: default avatarZach Pomerantz <zzmp@uniswap.org>

* Update src/tracing/errors.test.ts
Co-authored-by: default avatarZach Pomerantz <zzmp@uniswap.org>

* rm unnecessary

* change

* rm any

* cast earlier

* fixes

* fix: typings

* fixes

* rm mock

* rm comment

* try polyfill

* fix mocks

* rm stuff

* move mock

---------
parent 9e31756c
...@@ -2,6 +2,11 @@ import { ErrorEvent } from '@sentry/types' ...@@ -2,6 +2,11 @@ import { ErrorEvent } from '@sentry/types'
import { filterKnownErrors } from './errors' import { filterKnownErrors } from './errors'
Object.defineProperty(window.performance, 'getEntriesByType', {
writable: true,
value: jest.fn(),
})
describe('filterKnownErrors', () => { describe('filterKnownErrors', () => {
const ERROR = {} as ErrorEvent const ERROR = {} as ErrorEvent
it('propagates an error', () => { it('propagates an error', () => {
...@@ -35,6 +40,65 @@ describe('filterKnownErrors', () => { ...@@ -35,6 +40,65 @@ describe('filterKnownErrors', () => {
expect(filterKnownErrors(ERROR, { originalException })).toBe(null) expect(filterKnownErrors(ERROR, { originalException })).toBe(null)
}) })
describe('chunk errors', () => {
afterEach(() => {
jest.restoreAllMocks()
})
it('filters 499 error coded chunk error', () => {
jest.spyOn(window.performance, 'getEntriesByType').mockReturnValue([
{
name: 'https://app.uniswap.org/static/js/20.d55382e0.chunk.js',
responseStatus: 499,
} as PerformanceEntry,
])
const originalException = new Error(
'Loading chunk 20 failed. (error: https://app.uniswap.org/static/js/20.d55382e0.chunk.js)'
)
expect(filterKnownErrors(ERROR, { originalException })).toBeNull()
})
it('keeps error when status is different than 499', () => {
jest.spyOn(window.performance, 'getEntriesByType').mockReturnValue([
{
name: 'https://app.uniswap.org/static/js/20.d55382e0.chunk.js',
responseStatus: 200,
} as PerformanceEntry,
])
const originalException = new Error(
'Loading chunk 20 failed. (error: https://app.uniswap.org/static/js/20.d55382e0.chunk.js)'
)
expect(filterKnownErrors(ERROR, { originalException })).not.toBeNull()
})
it('filters out error when resource is missing', () => {
jest.spyOn(window.performance, 'getEntriesByType').mockReturnValue([])
const originalException = new Error(
'Loading chunk 20 failed. (error: https://app.uniswap.org/static/js/20.d55382e0.chunk.js)'
)
expect(filterKnownErrors(ERROR, { originalException })).toBeNull()
})
it('filters out error when performance is undefined', () => {
const originalException = new Error(
'Loading chunk 20 failed. (error: https://app.uniswap.org/static/js/20.d55382e0.chunk.js)'
)
expect(filterKnownErrors(ERROR, { originalException })).toBeNull()
})
it('filters out error when responseStatus is undefined', () => {
jest.spyOn(window.performance, 'getEntriesByType').mockReturnValue([
{
name: 'https://app.uniswap.org/static/js/20.d55382e0.chunk.js',
} as PerformanceEntry,
])
const originalException = new Error(
'Loading chunk 20 failed. (error: https://app.uniswap.org/static/js/20.d55382e0.chunk.js)'
)
expect(filterKnownErrors(ERROR, { originalException })).toBeNull()
})
})
describe('Content Security Policy', () => { describe('Content Security Policy', () => {
it('filters unsafe-eval evaluate errors', () => { it('filters unsafe-eval evaluate errors', () => {
const originalException = new Error( const originalException = new Error(
......
import { ClientOptions, ErrorEvent, EventHint } from '@sentry/types' import { ClientOptions, ErrorEvent, EventHint } from '@sentry/types'
import { didUserReject } from 'utils/swapErrorToUserReadableMessage' import { didUserReject } from 'utils/swapErrorToUserReadableMessage'
/* `responseStatus` is only currently supported on certain browsers.
* see: https://caniuse.com/mdn-api_performanceresourcetiming_responsestatus
*/
declare global {
interface PerformanceEntry {
responseStatus?: number
}
}
/** Identifies ethers request errors (as thrown by {@type import(@ethersproject/web).fetchJson}). */ /** Identifies ethers request errors (as thrown by {@type import(@ethersproject/web).fetchJson}). */
function isEthersRequestError(error: Error): error is Error & { requestBody: string } { function isEthersRequestError(error: Error): error is Error & { requestBody: string } {
return 'requestBody' in error && typeof (error as unknown as Record<'requestBody', unknown>).requestBody === 'string' return 'requestBody' in error && typeof (error as unknown as Record<'requestBody', unknown>).requestBody === 'string'
...@@ -40,6 +49,27 @@ export const filterKnownErrors: Required<ClientOptions>['beforeSend'] = (event: ...@@ -40,6 +49,27 @@ export const filterKnownErrors: Required<ClientOptions>['beforeSend'] = (event:
// If the error is based on a user rejecting, it should not be considered an exception. // If the error is based on a user rejecting, it should not be considered an exception.
if (didUserReject(error)) return null if (didUserReject(error)) return null
/*
* This ignores 499 errors, which are caused by Cloudflare when a request is cancelled.
* CF claims that some number of these is expected, and that they should be ignored.
* See https://groups.google.com/a/uniswap.org/g/cloudflare-eng/c/t3xvAiJFujY.
*/
if (error.message.match(/Loading chunk \d+ failed\. \(error: .+\.chunk\.js\)/)) {
const asset = error.message.match(/https?:\/\/.+?\.chunk\.js/)?.[0]
const entries = [...(performance?.getEntriesByType('resource') ?? [])]
const resource = entries?.find(({ name }) => name === asset)
const status = resource?.responseStatus
/*
* If the status if 499, then we ignore.
* If there's no status (meaning the browser doesn't support `responseStatus`) then we also ignore.
* These errors are likely also 499 errors, and we can catch any spikes in non-499 chunk errors via other browsers.
*/
if (!status || status === 499) {
return null
}
}
/* /*
* This is caused by HTML being returned for a chunk from Cloudflare. * This is caused by HTML being returned for a chunk from Cloudflare.
* Usually, it's the result of a 499 exception right before it, which should be handled. * Usually, it's the result of a 499 exception right before it, which should be handled.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment