Files
twenty/packages/twenty-front/src/modules/error-handler/components/SentryInitEffect.tsx
T
Sonarly Claude Code 1417d8003b chore: improve monitoring for fix: add error handling to reCAPTCHA execute promi
Added `ignoreErrors: [/invalid origin/i]` to the Sentry `init()` configuration in `SentryInitEffect.tsx`. This filters out the "invalid origin" error from reCAPTCHA at the Sentry SDK level, preventing it from being captured even if the `.catch()` handler is somehow bypassed (e.g., by the periodic token refresh interval in `CaptchaProviderScriptLoaderEffect`). This is a defense-in-depth measure — the code fix prevents the error from becoming an unhandled rejection, and this monitoring fix ensures any residual captures are filtered.
2026-03-21 03:26:21 +00:00

117 lines
3.8 KiB
TypeScript

import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useEffect, useState } from 'react';
import { isNonEmptyString } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
export const SentryInitEffect = () => {
const sentryConfig = useAtomStateValue(sentryConfigState);
const currentUser = useAtomStateValue(currentUserState);
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState);
const [isSentryInitialized, setIsSentryInitialized] = useState(false);
const [isSentryInitializing, setIsSentryInitializing] = useState(false);
const [isSentryUserDefined, setIsSentryUserDefined] = useState(false);
useEffect(() => {
const initializeSentry = async () => {
if (
isNonEmptyString(sentryConfig?.dsn) &&
!isSentryInitialized &&
!isSentryInitializing
) {
setIsSentryInitializing(true);
try {
const {
init,
browserTracingIntegration,
replayIntegration,
globalHandlersIntegration,
} = await import('@sentry/react');
init({
environment: sentryConfig?.environment ?? undefined,
release: sentryConfig?.release ?? undefined,
dsn: sentryConfig?.dsn,
integrations: [
browserTracingIntegration({}),
replayIntegration(),
globalHandlersIntegration({
onunhandledrejection: false, // handled in PromiseRejectionEffect
}),
],
tracePropagationTargets: [
'localhost:3001',
REACT_APP_SERVER_BASE_URL,
],
ignoreErrors: [
// reCAPTCHA errors when the domain is not registered in the site key's allowed domains
/invalid origin/i,
],
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
setIsSentryInitialized(true);
} catch (error) {
// oxlint-disable-next-line no-console
console.error('Failed to initialize Sentry:', error);
} finally {
setIsSentryInitializing(false);
}
}
};
const updateSentryUser = async () => {
if (
isSentryInitialized &&
isDefined(currentUser) &&
!isSentryUserDefined
) {
try {
const { setUser } = await import('@sentry/react');
setUser({
email: currentUser?.email,
id: currentUser?.id,
workspaceId: currentWorkspace?.id,
workspaceMemberId: currentWorkspaceMember?.id,
});
setIsSentryUserDefined(true);
} catch (error) {
// oxlint-disable-next-line no-console
console.error('Failed to set Sentry user:', error);
}
} else if (!isDefined(currentUser) && isSentryInitialized) {
try {
const { setUser } = await import('@sentry/react');
setUser(null);
} catch (error) {
// oxlint-disable-next-line no-console
console.error('Failed to clear Sentry user:', error);
}
}
};
initializeSentry();
updateSentryUser();
}, [
sentryConfig,
isSentryInitialized,
isSentryInitializing,
currentUser,
currentWorkspace,
currentWorkspaceMember,
isSentryUserDefined,
]);
return <></>;
};