filter network errors globally in sentry

This commit is contained in:
Samuel Newman
2026-08-28 14:59:16 +01:00
parent 076cdd650d
commit 1d136b07ed
8 changed files with 271 additions and 30 deletions
+102
View File
@@ -0,0 +1,102 @@
import {type Procedure, XrpcFetchError, XrpcResponseError} from '@atproto/lex'
import {describe, expect, test} from '@jest/globals'
import {type ErrorEvent} from '@sentry/react-native'
import {
dropExpectedNetworkErrors,
isExpectedSentryNetworkError,
} from '#/logger/sentry/network-errors'
const method = {
nsid: 'app.bsky.test.getTest',
type: 'query',
} as unknown as Procedure
function xrpcResponseError(status: number) {
return new XrpcResponseError(method, new Response(null, {status}), undefined)
}
describe('isExpectedSentryNetworkError', () => {
test.each([502, 503, 504])('detects transient upstream status %s', status => {
expect(isExpectedSentryNetworkError(xrpcResponseError(status))).toBe(true)
})
test.each([429, 500])('keeps actionable HTTP status %s', status => {
expect(isExpectedSentryNetworkError(xrpcResponseError(status))).toBe(false)
})
test('checks an XRPC fetch error cause', () => {
expect(
isExpectedSentryNetworkError(
new XrpcFetchError(method, new Error('fetch failed: connection reset')),
),
).toBe(true)
})
test('keeps implementation errors wrapped by XRPC fetch errors', () => {
expect(
isExpectedSentryNetworkError(
new XrpcFetchError(
method,
new TypeError('URL.canParse is not a function'),
),
),
).toBe(false)
})
})
describe('dropExpectedNetworkErrors', () => {
test('drops automatic captures using the original exception', () => {
const event = {type: undefined} satisfies ErrorEvent
expect(
dropExpectedNetworkErrors(event, {
originalException: new Error('fetch failed: connection closed'),
}),
).toBeNull()
})
test('checks every exception in a linked error chain', () => {
const event = {
type: undefined,
exception: {
values: [
{type: 'Error', value: 'Network request failed'},
{
type: 'XrpcInternalError',
value: 'Unable to fulfill XRPC request',
},
],
},
} satisfies ErrorEvent
expect(dropExpectedNetworkErrors(event, {})).toBeNull()
})
test('drops serialized transient upstream errors', () => {
const event = {
type: undefined,
exception: {
values: [{type: 'XrpcResponseError', value: 'Upstream Timeout'}],
},
} satisfies ErrorEvent
expect(dropExpectedNetworkErrors(event, {})).toBeNull()
})
test('keeps non-network events', () => {
const event = {
type: undefined,
exception: {
values: [
{
type: 'XrpcFetchError',
value: 'URL.canParse is not a function',
},
],
},
} satisfies ErrorEvent
expect(dropExpectedNetworkErrors(event, {})).toBe(event)
})
})