Skip to content

Commit a60f4f7

Browse files
dschomclaude
andcommitted
fix(settings): enrich Sentry errors in fetchLegalTerms and fetchConfig with timing and cancellation info
Adds fetchDuration (ms), cancelled (AbortError detection), and errorName to the Sentry captureException extra context so 'Load failed' errors are easier to triage by cause and duration. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent b2907a1 commit a60f4f7

2 files changed

Lines changed: 58 additions & 16 deletions

File tree

packages/fxa-settings/src/models/hooks.test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,13 @@ describe('useCmsInfoState', () => {
321321
// why we changed the hook to start with.
322322
expect(Sentry.captureException).toHaveBeenCalledWith(fetchError, {
323323
tags: { area: 'useCmsInfoState' },
324-
extra: { clientId: '1234567890abcdef', entrypoint: 'preferences' },
324+
extra: {
325+
clientId: '1234567890abcdef',
326+
entrypoint: 'preferences',
327+
cancelled: false,
328+
errorName: 'Error',
329+
fetchDuration: expect.any(Number),
330+
},
325331
});
326332
});
327333

packages/fxa-settings/src/models/hooks.ts

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ import {
1717
} from '../lib/integrations';
1818
import { ReachRouterWindow } from '../lib/window';
1919
import { StorageData, UrlHashData, UrlQueryData } from '../lib/model-data';
20-
import { MetricsData, SignedInAccountStatus } from '../components/App/interfaces';
20+
import {
21+
MetricsData,
22+
SignedInAccountStatus,
23+
} from '../components/App/interfaces';
2124
import {
2225
RelierClientInfo,
2326
RelierSubscriptionInfo,
@@ -28,7 +31,10 @@ import * as Sentry from '@sentry/browser';
2831
import { useDynamicLocalization } from '../contexts/DynamicLocalizationContext';
2932
import { sessionToken } from '../lib/cache';
3033
import { useLocalStorageSync } from '../lib/hooks/useLocalStorageSync';
31-
import { getFullAccountData, isSignedIn as checkIsSignedIn } from '../lib/account-storage';
34+
import {
35+
getFullAccountData,
36+
isSignedIn as checkIsSignedIn,
37+
} from '../lib/account-storage';
3238

3339
const DEFAULT_CMS_ENTRYPOINT = 'default';
3440

@@ -197,15 +203,21 @@ export function useInitialMetricsQueryState() {
197203
throw new Error('AuthClient not available');
198204
}
199205

200-
const [accountResult, totpResult, recoveryKeyResult] = await Promise.allSettled([
201-
authClient.account(token),
202-
authClient.checkTotpTokenExists(token),
203-
authClient.recoveryKeyExists(token, undefined),
204-
]);
205-
206-
const accountData = accountResult.status === 'fulfilled' ? accountResult.value : null;
207-
const totpData = totpResult.status === 'fulfilled' ? totpResult.value : null;
208-
const recoveryKeyData = recoveryKeyResult.status === 'fulfilled' ? recoveryKeyResult.value : null;
206+
const [accountResult, totpResult, recoveryKeyResult] =
207+
await Promise.allSettled([
208+
authClient.account(token),
209+
authClient.checkTotpTokenExists(token),
210+
authClient.recoveryKeyExists(token, undefined),
211+
]);
212+
213+
const accountData =
214+
accountResult.status === 'fulfilled' ? accountResult.value : null;
215+
const totpData =
216+
totpResult.status === 'fulfilled' ? totpResult.value : null;
217+
const recoveryKeyData =
218+
recoveryKeyResult.status === 'fulfilled'
219+
? recoveryKeyResult.value
220+
: null;
209221

210222
if (mounted && accountData) {
211223
const emails = accountData.emails || [];
@@ -215,10 +227,16 @@ export function useInitialMetricsQueryState() {
215227
account: {
216228
uid: accountData.uid,
217229
recoveryKey: recoveryKeyData
218-
? { exists: recoveryKeyData.exists, estimatedSyncDeviceCount: recoveryKeyData.estimatedSyncDeviceCount }
230+
? {
231+
exists: recoveryKeyData.exists,
232+
estimatedSyncDeviceCount:
233+
recoveryKeyData.estimatedSyncDeviceCount,
234+
}
219235
: null,
220236
metricsEnabled: accountData.metricsEnabled ?? true,
221-
primaryEmail: emails.find((e: { isPrimary?: boolean }) => e.isPrimary) || null,
237+
primaryEmail:
238+
emails.find((e: { isPrimary?: boolean }) => e.isPrimary) ||
239+
null,
222240
emails,
223241
totp: totpData || null,
224242
},
@@ -388,6 +406,7 @@ export function useCmsInfoState() {
388406
setState((prev) => ({ ...prev, loading: true }));
389407

390408
const fetchConfig = async () => {
409+
const fetchStart = performance.now();
391410
try {
392411
const url = new URL(`${authUrl}/v1/cms/config`);
393412
url.searchParams.append('clientId', clientId);
@@ -420,9 +439,17 @@ export function useCmsInfoState() {
420439
});
421440
}
422441
} catch (error) {
442+
const fetchDuration = Math.round(performance.now() - fetchStart);
443+
const cancelled = error instanceof Error && error.name === 'AbortError';
423444
Sentry.captureException(error, {
424445
tags: { area: 'useCmsInfoState' },
425-
extra: { clientId, entrypoint },
446+
extra: {
447+
clientId,
448+
entrypoint,
449+
fetchDuration,
450+
cancelled,
451+
errorName: error instanceof Error ? error.name : typeof error,
452+
},
426453
});
427454

428455
if (mounted) {
@@ -579,6 +606,7 @@ export function useLegalTermsState() {
579606
setState((prev) => ({ ...prev, loading: true }));
580607

581608
const fetchLegalTerms = async () => {
609+
const fetchStart = performance.now();
582610
try {
583611
const url = new URL(`${authUrl}/v1/cms/legal-terms`);
584612
url.searchParams.append(queryParam, queryValue);
@@ -622,9 +650,17 @@ export function useLegalTermsState() {
622650
});
623651
}
624652
} catch (error) {
653+
const fetchDuration = Math.round(performance.now() - fetchStart);
654+
const cancelled = error instanceof Error && error.name === 'AbortError';
625655
Sentry.captureException(error, {
626656
tags: { area: 'useLegalTermsState' },
627-
extra: { queryParam, queryValue },
657+
extra: {
658+
queryParam,
659+
queryValue,
660+
fetchDuration,
661+
cancelled,
662+
errorName: error instanceof Error ? error.name : typeof error,
663+
},
628664
});
629665

630666
if (mounted) {

0 commit comments

Comments
 (0)