Skip to content

fix: make Event phase constants writable/configurable to prevent TypeError - #57199

Open
IsaacIsrael wants to merge 3 commits into
react:mainfrom
IsaacIsrael:fix/event-readonly-properties
Open

fix: make Event phase constants writable/configurable to prevent TypeError#57199
IsaacIsrael wants to merge 3 commits into
react:mainfrom
IsaacIsrael:fix/event-readonly-properties

Conversation

@IsaacIsrael

@IsaacIsrael IsaacIsrael commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #54732

The Event class in src/private/webapis/dom/events/Event.js defines NONE, CAPTURING_PHASE, AT_TARGET, and BUBBLING_PHASE as both readonly instance fields and via Object.defineProperty without writable/configurable flags. When Hermes compiles these class fields, they become non-writable/non-configurable properties, causing a TypeError at instantiation time:

TypeError: Cannot assign to read-only property 'NONE'
    at Event (Event.js:53:4)
    at anonymous (WebSocket.js:258:63)
    at apply (native)
    at emit (EventEmitter.js:130:7)

This crashes whenever a WebSocket event fires (Metro dev connection, push notifications), and the unhandled TypeError propagates into any concurrent Promise chain — breaking fetch() uploads and other network operations.

Changes

  1. Remove readonly from instance-level field declarations — the static properties are inherited via the prototype chain automatically, so explicit readonly instance declarations are unnecessary and cause the Hermes conflict.

  2. Add writable: true, configurable: true to all 8 Object.defineProperty calls — allows event-target-shim (used by abort-controller / fetch()) to redefine these properties when needed.

  3. Replace Event.NONE initializer with literal 0 — the [EVENT_PHASE_KEY] field initializer was referencing Event.NONE before Object.defineProperty defines it at the bottom of the file.

Changelog:

[GENERAL] [FIXED] - Fix TypeError "Cannot assign to read-only property 'NONE'" in Event class when using New Architecture with Hermes

Test Plan

  1. Create a React Native 0.85+ app with New Architecture enabled
  2. Make any fetch() request or open a WebSocket connection
  3. Verify no TypeError: Cannot assign to read-only property 'NONE' is thrown
  4. Verify event.NONE === 0, event.CAPTURING_PHASE === 1, event.AT_TARGET === 2, event.BUBBLING_PHASE === 3 still hold on Event instances

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 14, 2026
…Error

The Event class defines NONE, CAPTURING_PHASE, AT_TARGET, BUBBLING_PHASE
as readonly instance fields and via Object.defineProperty without
writable/configurable flags. Hermes compiles these as non-writable,
causing TypeError at instantiation: Cannot assign to read-only property NONE.

This crashes on WebSocket events and propagates into fetch uploads.

Fix: remove readonly from instance fields, add writable+configurable to
Object.defineProperty calls, replace Event.NONE initializer with literal 0.

Fixes react#54732

Co-authored-by: Cursor <cursoragent@cursor.com>
@IsaacIsrael
IsaacIsrael force-pushed the fix/event-readonly-properties branch from 5f3593f to 2dafc35 Compare June 14, 2026 14:00
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jun 14, 2026
IsaacIsrael and others added 2 commits June 15, 2026 13:08
Flow needs the instance-level field declarations (NONE, CAPTURING_PHASE,
AT_TARGET, BUBBLING_PHASE) to know these properties exist on Event
instances. The previous commit removed them entirely, causing 12 Flow
prop-missing errors. This restores them without the `readonly` modifier
so Hermes won't create non-writable properties that conflict with the
Object.defineProperty calls.

Co-authored-by: Cursor <cursoragent@cursor.com>
The instance-level declarations need `readonly` to match their literal
types (0, 1, 2, 3). Without readonly, Flow treats them as invariant and
rejects the Object.defineProperty value assignments as incompatible-type.
The readonly annotation is type-level only (stripped at runtime); the
actual runtime fix is writable+configurable on Object.defineProperty.

Co-authored-by: Cursor <cursoragent@cursor.com>
@fabriziocucci

Copy link
Copy Markdown
Contributor

Thanks for digging into this, the crash in #54732 is real and the stack trace points right at it.

I traced the fix and I think it clears the crash but changes the value of the instance constants in a way that might not be intended. Sharing what I found in case it is useful.

RN compiles class fields in loose mode, so the instance declaration readonly NONE: 0; becomes this.NONE = void 0 in the constructor. Today that assignment throws because the prototype NONE is non-writable, which is the crash you hit. Adding writable: true makes the assignment succeed, but it then creates an own NONE property set to undefined that shadows the 0 on the prototype. So after the change new Event().NONE is undefined instead of 0 (same for CAPTURING_PHASE, AT_TARGET and BUBBLING_PHASE).

I checked this against the transpiled output with a small repro:

  • before: new Event() throws Cannot assign to read only property 'NONE'
  • with writable: true: no throw, but event.NONE === undefined
  • with the instance fields as declare: no throw, and event.NONE === 0

The existing tests would not catch this since they assert the static Event.CAPTURING_PHASE and the eventPhase getter, not the instance constants.

One option that fixes the crash and keeps event.NONE === 0 is to make the four instance declarations type-only with declare:

declare readonly NONE: 0;
declare readonly CAPTURING_PHASE: 1;
declare readonly AT_TARGET: 2;
declare readonly BUBBLING_PHASE: 3;

A declare field emits no runtime assignment, so there is no this.NONE = to fail and instances keep inheriting the values from the prototype. With that you also would not need the writable: true / configurable: true additions, so the constants stay read-only like the DOM spec.

I might be missing context on why the readonly instance fields were kept, so let me know if there is a Flow reason declare does not work here.

@fabriziocucci
fabriziocucci self-requested a review July 20, 2026 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Event.NONE/CAPTURING_PHASE/AT_TARGET/BUBBLING_PHASE properties missing configurable: true breaks event-target-shim

2 participants