Skip to content

Commit a3be8f2

Browse files
fix(telemetry): stop flushErrorReports throwing out of the boot path
Self-review catch. `reportError` guarded its dispatch but `flushErrorReports` did not, and that one is called directly from `main.ts` and `bootstrap.ts`. A sink that threw during the flush would have taken the whole app down instead of the single report it failed to deliver — the exact failure the module exists to prevent. Also stops forwarding an explicit `level: undefined` into the RUM context when no level was given. Test verified red-green: it fails with the guard removed.
1 parent a1c84be commit a3be8f2

2 files changed

Lines changed: 25 additions & 2 deletions

File tree

src/platform/telemetry/reportError.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,21 @@ describe('reportError', () => {
137137
expect(context).toMatchObject({ api_endpoint: '/settings/{key}' })
138138
})
139139

140+
it('does not throw out of flushErrorReports when a sink throws', async () => {
141+
sentryLive(false)
142+
datadogLive(false)
143+
const { reportError, flushErrorReports } = await loadReportError()
144+
145+
reportError(new Error('early'), { errorType: 'resource_load_error' })
146+
147+
datadogLive(true)
148+
addError.mockImplementation(() => {
149+
throw new Error('datadog exploded')
150+
})
151+
152+
expect(() => flushErrorReports()).not.toThrow()
153+
})
154+
140155
it('does not throw when a sink throws', async () => {
141156
captureException.mockImplementation(() => {
142157
throw new Error('sentry exploded')

src/platform/telemetry/reportError.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function dispatch(error: Error, options: ReportErrorOptions): boolean {
5757
...context,
5858
...tags,
5959
error_type: errorType,
60-
level
60+
...(level ? { level } : {})
6161
})
6262
}
6363

@@ -67,14 +67,22 @@ function dispatch(error: Error, options: ReportErrorOptions): boolean {
6767
/**
6868
* Drains reports buffered before a sink came up. Safe to call repeatedly;
6969
* a no-op while every sink is still inert.
70+
*
71+
* Callers are `main.ts` and `bootstrap.ts` on the boot path, so this must
72+
* never throw: a sink that explodes here would take the whole app down
73+
* instead of the one report it failed to deliver.
7074
*/
7175
export function flushErrorReports(): void {
7276
if (!pendingReports.length) return
7377
if (!isSentryEnabled() && !isDatadogRumLive()) return
7478

7579
const drained = pendingReports.splice(0, pendingReports.length)
7680
for (const { error, options } of drained) {
77-
dispatch(error, options)
81+
try {
82+
dispatch(error, options)
83+
} catch (reporterFailure) {
84+
console.error('[reportError] failed to flush', reporterFailure, error)
85+
}
7886
}
7987
}
8088

0 commit comments

Comments
 (0)