From f51454b2187788c5a6e6b17b55941a1c9e2beaac Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Thu, 23 Jul 2026 09:19:57 -0300 Subject: [PATCH 1/2] refactor(analytics): guard trackEvent and trackTraits with try/catch A failing tracker (GA or Amplitude) must never break the caller, e.g. the onboarding bootstrap firing milestone events. Co-Authored-By: Claude Fable 5 --- frontend/web/project/api.ts | 64 +++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/frontend/web/project/api.ts b/frontend/web/project/api.ts index 05678da4cb61..ba5a33e69371 100644 --- a/frontend/web/project/api.ts +++ b/frontend/web/project/api.ts @@ -309,27 +309,33 @@ const API = { label?: string extra?: Record }): void { - if (Project.ga) { - if (Project.logAnalytics) console.log('ANALYTICS EVENT', data) - if (!data || !data.category || !data.event) - return console.error('Invalid event provided', data) - if (data.category === 'First') - API.postEvent( - `${data.event}${data.extra ? ` ${JSON.stringify(data.extra)}` : ''}`, - 'first_events', - ) - ga('send', { - eventAction: data.event, - eventCategory: data.category, - eventLabel: data.label, - hitType: 'event', - }) - } - if (Project.amplitude) { - amplitude.track(data.event, { - category: data.category, - ...(data.extra || {}), - }) + try { + if (Project.ga) { + if (Project.logAnalytics) console.log('ANALYTICS EVENT', data) + if (!data || !data.category || !data.event) + return console.error('Invalid event provided', data) + if (data.category === 'First') + API.postEvent( + `${data.event}${ + data.extra ? ` ${JSON.stringify(data.extra)}` : '' + }`, + 'first_events', + ) + ga('send', { + eventAction: data.event, + eventCategory: data.category, + eventLabel: data.label, + hitType: 'event', + }) + } + if (Project.amplitude) { + amplitude.track(data.event, { + category: data.category, + ...(data.extra || {}), + }) + } + } catch (err) { + console.error('Error tracking event', err) } }, @@ -344,12 +350,16 @@ const API = { }, trackTraits(traits: Record): void { - if (Project.amplitude && traits) { - const identifyObj = new amplitude.Identify() - Object.entries(traits).forEach(([key, value]) => - identifyObj.set(key, value), - ) - amplitude.identify(identifyObj) + try { + if (Project.amplitude && traits) { + const identifyObj = new amplitude.Identify() + Object.entries(traits).forEach(([key, value]) => + identifyObj.set(key, value), + ) + amplitude.identify(identifyObj) + } + } catch (err) { + console.error('Error tracking traits', err) } }, } From 2d67667c0d7942f3cf7352b6ce6b2d11b71478e3 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Thu, 23 Jul 2026 09:20:00 -0300 Subject: [PATCH 2/2] fix(onboarding): tag the variant only after identified flags load The gate can mount before the identify fetch settles, which risks tagging onboarding_variant from stale or env-level flags. Wrap the gate in ConfigProvider so it re-renders on SDK fetches, and only write the trait once flags come from the server for the current identity. Co-Authored-By: Claude Fable 5 --- .../pages/onboarding/GettingStartedGate.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/web/components/pages/onboarding/GettingStartedGate.tsx b/frontend/web/components/pages/onboarding/GettingStartedGate.tsx index 4374867fd57a..97291785b515 100644 --- a/frontend/web/components/pages/onboarding/GettingStartedGate.tsx +++ b/frontend/web/components/pages/onboarding/GettingStartedGate.tsx @@ -1,4 +1,6 @@ import React, { FC, useEffect } from 'react' +import flagsmith from '@flagsmith/flagsmith' +import ConfigProvider from 'common/providers/ConfigProvider' import { getOnboardingVariant, isSinglePageOnboarding, @@ -8,13 +10,21 @@ import GettingStartedPage from 'components/pages/GettingStartedPage' import OnboardingFlow from './OnboardingFlow' const GettingStartedGate: FC = () => { + // ConfigProvider re-renders the gate on every SDK fetch; only tag the + // variant once the server has answered for this identity. + const trustworthy = + !flagsmith.loadingState?.isFetching && + flagsmith.loadingState?.source === 'SERVER' && + !!flagsmith.getContext().identity + const variant = getOnboardingVariant() useEffect(() => { + if (!trustworthy) return API.trackTraits({ onboarding_variant: variant }) - }, [variant]) + }, [trustworthy, variant]) return isSinglePageOnboarding() ? : } -export default GettingStartedGate +export default ConfigProvider(GettingStartedGate)