Skip to content

Commit 3800fcd

Browse files
committed
fix(analytics): guard PostHog session-recording APIs to prevent crash
Some users saw a crash: "window.posthog.stopSessionRecording is not a function". Different PostHog bundles/plugins expose session recording controls differently: - Some expose start/stop on the root (posthog.startSessionRecording / stopSessionRecording) - Others expose them under posthog.sessionRecording.startRecording / stopRecording - In all versions, recording can be toggled via set_config({ disable_session_recording }) Changes - Hardened enable/disable logic in [src/components/PostHogProvider/index.tsx](src/components/PostHogProvider/index.tsx): - Flexible interface: [PostHogInstance](src/components/PostHogProvider/index.tsx:9) with optional methods for multiple SDK shapes - Consent-driven control via [PostHogProvider()](src/components/PostHogProvider/index.tsx:31) - [enablePostHog()](src/components/PostHogProvider/index.tsx:54): - Best-effort ph.opt_in_capturing() (try/catch) - Use ph.set_config?.({ disable_session_recording: false }) - Attempt ph.startSessionRecording?.() and ph.sessionRecording?.startRecording?.() - [disablePostHog()](src/components/PostHogProvider/index.tsx:77): - Use ph.set_config?.({ disable_session_recording: true }) - Attempt ph.stopSessionRecording?.() and ph.sessionRecording?.stopRecording?.() - Best-effort ph.opt_out_capturing() (try/catch) - All calls guarded against SSR (typeof window) and undefined window.posthog Why - Avoids invoking undefined helpers in environments where the adapter/plugin does not expose them - set_config({ disable_session_recording }) is widely supported and reliably toggles recording - Preserves existing consent flow using [hasConsent()](src/lib/analytics/consent-manager.ts:38) and [onConsentChange()](src/lib/analytics/consent-manager.ts:48) User impact - Eliminates the crash while maintaining the intended consent behavior Files touched - [src/components/PostHogProvider/index.tsx](src/components/PostHogProvider/index.tsx) Notes - Editor TS errors for '@roo-code/types', 'react-cookie-consent', and 'tldts' are due to missing local node_modules; they are declared in package.json and resolve in CI/local installs. Test plan - Load with consent denied: set_config disables recording; no start/stop calls; no crash - Accept consent via banner: set_config enables recording; start methods called if present; no crash - Decline after accepting: set_config disables recording; stop methods called if present; no crash - Navigate/refresh to ensure stability across routes and SDK variants
1 parent 05feff6 commit 3800fcd

File tree

1 file changed

+54
-10
lines changed

1 file changed

+54
-10
lines changed

src/components/PostHogProvider/index.tsx

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
import React, { useEffect } from 'react';
22
import { hasConsent, onConsentChange } from '../../lib/analytics/consent-manager';
33

4-
// PostHog interface - define the methods we use
4+
// PostHog interface - define only what we use, and guard optional APIs
5+
type PostHogSetConfig = {
6+
disable_session_recording?: boolean;
7+
};
8+
59
interface PostHogInstance {
610
opt_in_capturing(): void;
711
opt_out_capturing(): void;
8-
startSessionRecording(): void;
9-
stopSessionRecording(): void;
12+
set_config?(config: PostHogSetConfig): void;
13+
14+
// Legacy/direct helpers that may or may not exist on the root instance
15+
startSessionRecording?: () => void;
16+
stopSessionRecording?: () => void;
17+
18+
// Namespaced session recording controls in some versions
19+
sessionRecording?: {
20+
startRecording?: () => void;
21+
stopRecording?: () => void;
22+
};
1023
}
1124

1225
declare global {
@@ -39,17 +52,48 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) {
3952
}, []);
4053

4154
const enablePostHog = () => {
42-
if (typeof window !== 'undefined' && window.posthog) {
43-
// Re-initialize PostHog if it was previously disabled
44-
window.posthog.opt_in_capturing();
45-
window.posthog.startSessionRecording();
55+
if (typeof window === 'undefined' || !window.posthog) return;
56+
const ph = window.posthog;
57+
58+
// Re-enable analytics if previously opted out
59+
try {
60+
ph.opt_in_capturing();
61+
} catch {
62+
// no-op
63+
}
64+
65+
// Ensure session recording is enabled via config; don't assume start API exists
66+
try {
67+
ph.set_config?.({ disable_session_recording: false });
68+
} catch {
69+
// no-op
4670
}
71+
72+
// Best-effort: try available start methods without crashing if missing
73+
ph.startSessionRecording?.();
74+
ph.sessionRecording?.startRecording?.();
4775
};
4876

4977
const disablePostHog = () => {
50-
if (typeof window !== 'undefined' && window.posthog) {
51-
window.posthog.opt_out_capturing();
52-
window.posthog.stopSessionRecording();
78+
if (typeof window === 'undefined' || !window.posthog) return;
79+
const ph = window.posthog;
80+
81+
// Disable session recording via config first (works across versions)
82+
try {
83+
ph.set_config?.({ disable_session_recording: true });
84+
} catch {
85+
// no-op
86+
}
87+
88+
// Best-effort: try available stop methods without crashing if missing
89+
ph.stopSessionRecording?.();
90+
ph.sessionRecording?.stopRecording?.();
91+
92+
// Opt out of all analytics capture if consent is denied
93+
try {
94+
ph.opt_out_capturing();
95+
} catch {
96+
// no-op
5397
}
5498
};
5599

0 commit comments

Comments
 (0)