-
Notifications
You must be signed in to change notification settings - Fork 83
Description
Sentry: SecurityError — removeEventListener from cross-origin Window
Sentry issue: https://flowfuse.sentry.io/issues/7303634221/
The Error
SecurityError: Failed to read a named property 'removeEventListener' from 'Window':
An attempt was made to break through the security policy of the user agent.
Observed route: Triggered when navigating away from /instance/:id/editor/*
Sentry breadcrumb example:
{
"from": "/instance/9e930e58-c9b2-478a-a630-8aa27cc72a11/editor/devices",
"to": "/instance/9e930e58-c9b2-478a-a630-8aa27cc72a11/overview"
}What the Error Means
A SecurityError: Failed to read a named property 'X' from 'Window' is thrown when code attempts to access a property on a cross-origin Window object — in this case the Node-RED editor iframe's contentWindow, which loads from instance.url (a different origin from the FlowFuse app). The browser's Same-Origin Policy blocks most property access on cross-origin windows, with the exception of a small allowlist (postMessage, close, focus, etc.).
Root Cause — PostHog Session Recording
PostHog's session recorder (@posthog/rrweb-record) is the likely source of this error. The Sentry stack trace shows the crash originating inside PostHog's rrweb-based recorder:
Crashed in non-app:
@posthog/rrweb-record@0.0.45 — rrweb-record.js (isAttachIframe: true)
LazyLoadedSessionRecording.stop
sentryWrapped (@sentry/browser)
PostHog uses rrweb for DOM recording and by default attempts to attach to iframes — including cross-origin ones — to capture their content. When a navigation causes the editor to unmount, LazyLoadedSessionRecording.stop() tries to detach event listeners from the Node-RED iframe's contentWindow, which the browser blocks with a SecurityError because the iframe is cross-origin.
The isAttachIframe: true flag confirms PostHog had successfully attached to the iframe and was now failing to detach.
Supporting evidence from the Sentry breadcrumbs
5:37:30.491 Sentry Transaction — user arrives at /editor/devices
5:37:32.051 Navigation — /editor/devices → /overview
5:37:32.121 SecurityError — 70ms after navigation starts
The ~70ms gap is consistent with synchronous teardown by the recording SDK during route transition.
The Fix
Clear the iframe src to about:blank in beforeUnmount, before PostHog's rrweb recorder attempts to detach its listeners. The fix applies to both editor wrapper components — one for hosted instances, one for remote/device instances — since both render a cross-origin iframe with the same name.
Files:
frontend/src/components/immersive-editor/HostedInstanceEditorWrapper.vuefrontend/src/components/immersive-editor/RemoteInstanceEditorWrapper.vue
beforeUnmount () {
// Clear the iframe src before unmount so PostHog's rrweb recorder can safely
// detach its event listeners. Without this, rrweb throws a SecurityError when
// calling contentWindow.removeEventListener on the cross-origin Node-RED iframe.
if (this.$refs.iframe) {
this.$refs.iframe.src = 'about:blank'
}
},about:blank is same-origin with any page, so by the time rrweb calls contentWindow.removeEventListener during teardown the iframe is no longer cross-origin and the browser allows the call. PostHog continues to record the iframe normally during the session — only the final frame as the user navigates away shows it going blank, which is imperceptible in practice.
Alternatives Considered
Option A — recordCrossOriginIframes: false in PostHog init
Setting session_recording: { recordCrossOriginIframes: false } in the PostHog init config (in forge/routes/ui/index.js) would prevent rrweb from ever attaching to the iframe, eliminating the SecurityError at the source. However, this was rejected because PostHog is currently successfully recording the Node-RED iframe and disabling cross-origin recording would silently drop that coverage. The beforeUnmount approach fixes the teardown issue without sacrificing any recording.
Option B — block: ['iframe'] in Sentry Replay
Configuring Sentry's Replay integration to block iframes (new Replay({ block: ['iframe'] })) would prevent Sentry Replay from attempting to access the iframe. However, Sentry Replay is a secondary suspect — the stack trace clearly shows PostHog's rrweb as the source. Changing the Sentry config would not fix the underlying PostHog teardown issue, just change which tool surfaces the error.
Option C — Upgrade posthog-js
The stack trace references @posthog/rrweb-record@0.0.45, which is a known buggy version (posthog-js#2533). However, the PostHog frontend script is loaded from the PostHog CDN (api_host + "/static/array.js") — it is not an npm dependency in this project. There is nothing to upgrade on our end; the version served is entirely controlled by PostHog and their issue remains open/unresolved.
Why This Error is Hard to Reproduce
Locally
The error requires the Node-RED editor iframe to be loaded from a different origin than the FlowFuse app. In local development this condition is typically not met — either there is no live instance running, or the iframe never loads. The error will only appear in staging or production where a real instance is running at a cross-origin URL (e.g. https://my-instance.flowfuse.cloud).
On staging / production
Even with a real instance, the error is only triggered when PostHog's session recorder is actively recording the session. PostHog's recording rate is configured separately from Sentry's, so the exact proportion of affected sessions depends on PostHog's sample rate. The 2 captured Sentry errors represent only a fraction of actual occurrences — Sentry only surfaces the error when both PostHog recording and Sentry error reporting are active simultaneously.
To verify the fix on staging, ensure PostHog session recording is active and navigate from the editor to the instance overview. The SecurityError should no longer appear in Sentry after deploying.
References
- PostHog/posthog-js#2533 — SecurityError on idle (open bug)
- PostHog: Iframe recording
- PostHog: Session Replay troubleshooting
- Sentry Session Replay Troubleshooting — Vue
- MDN: Same-Origin Policy
Epic/Story
No response
Have you provided an initial effort estimate for this issue?
I have provided an initial effort estimate
Metadata
Metadata
Assignees
Labels
Type
Projects
Status
Status