Skip to content

Bug: [19.2] DEV-build logComponentRender throws SecurityError on cross-origin Window props #36430

@Rasvom

Description

@Rasvom

React 19.2 DEV-build crashes the entire fiber-tree (Should not already be working) when a component receives a cross-origin Window object (e.g. iframe.contentWindow of an iframe with srcdoc="") as a prop. The new logComponentRender (called from commitPassiveMountOnFiber) recursively walks props in addObjectToProperties / addValueToProperties and reads value.$$typeof without try/catch. For a cross-origin Window any property access throws SecurityError, corrupting the work-in-progress fiber.

This is not the React DevTools extension issue (#29011) — logComponentRender is part of the native DEV-build performance logger introduced in 19.2. It runs unconditionally in development, without any browser extension.

React version: 19.2.5 (react@19.2.5, react-dom@19.2.5)

Steps To Reproduce

  1. Bootstrap a fresh Vite + React 19.2.5 app: npm create vite@latest -- --template react-ts, then install react@19.2.5 and react-dom@19.2.5.
  2. Replace App.tsx with the code from the Link to code example section below.
  3. Run npm run dev and open the page in Chrome (any recent version).
  4. Right after mount the page freezes — clicks and inputs do nothing.
  5. Open DevTools console and see two errors stacked:
    • Uncaught SecurityError: Failed to read a named property '$$typeof' from 'Window' at logComponentRendercommitPassiveMountOnFiber
    • Uncaught Error: Should not already be working. at performWorkOnRoot

Reproduces 100% in DEV-build. Production build (npm run build && npm run preview) is unaffected.

Link to code example

import { useEffect, useRef, useState } from 'react';

function App() {
  const iframeRef = useRef<HTMLIFrameElement>(null);
  const [win, setWin] = useState<Window | null>(null);

  useEffect(() => {
    // iframe with srcdoc="" gets origin "null" — cross-origin to host page
    setWin(iframeRef.current?.contentWindow ?? null);
  }, []);

  return (
    <>
      <iframe ref={iframeRef} srcDoc="<p>hi</p>" title="x" />
      {/* Pass cross-origin Window as a prop. React DEV-build's
          logComponentRender walks props of <Child> and reads
          win.$$typeof → SecurityError. */}
      <Child win={win} />
    </>
  );
}

function Child({ win }: { win: Window | null }) {
  return <div>{win ? 'has window' : 'no window'}</div>;
}

export default App;

The current behavior

addObjectToProperties in react-dom/cjs/react-dom-client.development.js (around line 3783) does an unguarded for (var key in object) loop, then addValueToProperties (around line 3789) reads value.$$typeof without try/catch. For a cross-origin Window any property access throws SecurityError, which propagates out of the commit phase and leaves the work-in-progress fiber broken. All subsequent renders throw Should not already be working, and the UI becomes completely unresponsive — no clicks, no input.

Stack:

Uncaught SecurityError: Failed to read a named property '$$typeof' from 'Window':
  Blocked a frame with origin "http://localhost:5173" from accessing a cross-origin frame.
    at addObjectToProperties (react-dom-client.development.js)
    at addValueToProperties
    at addObjectToProperties
    at addValueToProperties
    at addObjectDiffToProperties
    at logComponentRender
    at commitPassiveMountOnFiber
    at recursivelyTraversePassiveMountEffects

Uncaught Error: Should not already be working.
    at performWorkOnRoot
    at performWorkOnRootViaSchedulerTask

Real-world impact: embedded notebook editors, dashboards, and preview-style tooling that store DOM or Window refs in component state (Plotly, Vega, marimo HTML widgets, Jupyter widget conversions) fail to render after first mount. The error message is misleading — the root cause is React's render-logger, not user code.

The expected behavior

addObjectToProperties and addValueToProperties should wrap property access in try/catch and treat unreadable values as an opaque marker (e.g. [CrossOriginFrame]), so the render-logger never throws. The render-logger is a DEV-only diagnostic and must not corrupt the fiber tree when iterating props.

A working workaround: wrap the body of addObjectToProperties in try/catch. Verified by patching the pre-bundled chunk via an esbuild plugin in a Vite app — fixes all symptoms in DEV without touching the rest of the build.

Related issues (different root cause but similar symptom):

Metadata

Metadata

Assignees

No one assigned

    Labels

    Status: UnconfirmedA potential issue that we haven't yet confirmed as a bug

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions