Skip to content

[pull] main from expo:main#824

Merged
pull[bot] merged 2 commits intocode:mainfrom
expo:main
May 2, 2026
Merged

[pull] main from expo:main#824
pull[bot] merged 2 commits intocode:mainfrom
expo:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 2, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

intergalacticspacehighway and others added 2 commits May 2, 2026 05:55
#45072)

# Why

                  
Concurrent `Record.toDictionary` calls on Records sharing `@Field` class
instances (e.g. expo-video's VideoTrack payloads emitted from multiple
background Tasks) causes crash
#40688 (comment). Also
@AdityaPahilwani reached out to me with below stack trace that happens
because of the same issue (concurrent video events dispatched from
different queues that uses same field/record)

```
Thread 54 Crashed:
0   Quince                              0x00000001044a0c30 Field.key.getter + 56
1   Quince                              0x00000001044a0c30 Field.key.getter + 56
2   Quince                              0x00000001044a0c30 Field.key.getter + 56 (Field.swift:19)
3   Quince                              0x00000001044a1508 protocol witness for AnyFieldInternal.key.getter in conformance Field<A> + 20
4   Quince                              0x00000001044bae98 closure #1 (String?, Any) in fieldsOf(Record) + 0 (Record.swift:77)
5   Quince                              0x00000001044bae98 specialized fieldsOf(Record) + 456
6   Quince                              0x00000001044bae98 specialized fieldsOf(Record) + 456
7   Quince                              0x00000001044bae98 specialized fieldsOf(Record) + 456 (Record.swift:76)
8   Quince                              0x00000001044ba578 Record.toDictionary(appContext: AppContext?) + 32
9   Quince                              0x00000001044ba578 Record.toDictionary(appContext: AppContext?) + 32 (Record.swift:63)
10  Quince                              0x000000010448d088 DynamicConvertibleType.convertResult<A>(_: A, appContext: AppContext) + 204 (DynamicConvertibleType.swift:26)
11  Quince                              0x000000010448d19c protocol witness for AnyDynamicType.convertResult<A>(_: A1, appContext: AppContext) in conformance DynamicConvertibleType + 16
12  Quince                              0x000000010449053c DynamicOptionalType.convertResult<A>(_: A, appContext: AppContext) + 100 (DynamicOptionalType.swift:40)
13  Quince                              0x000000010449064c protocol witness for AnyDynamicType.convertResult<A>(_: A1, appContext: AppContext) in conformance DynamicOptionalType + 12
14  Quince                              0x00000001044c3660 $s15ExpoModulesCore11ConversionsV21convertFunctionResult_10appContext11dynamicTypeypxSg_AA03AppI0CSgAA010AnyDynamicK0_pSgtlFZyp_Tt3g5 + 628 (Conversions.swift:193)
15  Quince                              0x00000001044bac30 closure #1 (inout [String : Any], AnyFieldInternal) in Record.toDictionary(appContext: AppContext?) + 164
16  Quince                              0x00000001044bb43c partial apply for closure #1 (inout [String : Any], AnyFieldInternal) in Record.toDictionary(appContext: AppContext?) + 16
17  libswiftCore.dylib                  0x00000001920dd148 Sequence.reduce<A>(into: __owned A1, _: (inout A1, A.Element)) + 764
18  Quince                              0x00000001044ba5e0 Record.toDictionary(appContext: AppContext?) + 136
19  Quince                              0x00000001044f6820 VideoPlayer.safeEmit(event: String, payload: Record?) + 160 (VideoPlayer.swift:422)
20  Quince                              0x00000001044f6fec VideoPlayer.currentVideoTrack.setter + 356
21  Quince                              0x00000001044f87f8 protocol witness for VideoPlayerObserverDelegate.onVideoTrackChanged(player: AVPlayer, oldVideoTrack: VideoTrack?, newVideoTrack: VideoTrack?) in conformance VideoPlayer + 48
22  Quince                              0x00000001044fe648 closure #1 (WeakPlayerObserverDelegate) in VideoPlayerObserver.currentVideoTrack.didset + 0 (VideoPlayerObserver.swift:87)
23  Quince                              0x00000001044fe648 VideoPlayerObserver.currentVideoTrack.didset + 896
24  Quince                              0x00000001044fe648 VideoPlayerObserver.currentVideoTrack.didset + 896 (VideoPlayerObserver.swift:86)
25  Quince                              0x00000001045005a4 closure #1 () in closure #4 @sendable (AVPlayerItem, NSKeyValueObservedChange<[AVPlayerItemTrack]>) in VideoPlayerObserver.initializeCurrentPlayerItemObservers(player: AVPlayer, playerItem: AVPlayerItem) + 160
26  libswift_Concurrency.dylib          0x000000019336a8b4 swift::runJobInEstablishedExecutorContext(swift::Job*) + 288
27  libswift_Concurrency.dylib          0x000000019336bd28 swift_job_runImpl(swift::Job*, swift::SerialExecutorRef) + 156
28  libdispatch.dylib                   0x00000001cdbc2f48 _dispatch_root_queue_drain + 364
29  libdispatch.dylib                   0x00000001cdbc36fc _dispatch_worker_thread2 + 180
30  libsystem_pthread.dylib             0x00000001f19fd37c _pthread_wqthread + 232
31  libsystem_pthread.dylib             0x00000001f19fc8c0 start_wqthread + 8
```

I managed to repro it (not the exact expo video crash but conditions
under which it can happen) with an added testcase. The crash points to
the `field.options` set call.

<img width="300" height="auto" alt="Screenshot 2026-04-24 at 8 59 31 PM"
src="https://github.com/user-attachments/assets/25a309fb-aaf5-4884-9c53-26343d86eda8"
/>


<!--
Please describe the motivation for this PR, and link to relevant GitHub
issues, forums posts, or feature requests.
-->

# How

Made `options` property private. Read and write needs to happen via
`withOptions` function, which provides locked access to the `options`
property

<!--
How did you build this feature or fix this bug and why?
-->

# Test Plan

Added a testcase for the crash. Run the tests with and without changes
in Record.swift. When trying to repro the crash on test run it 2-3 times
if not reproducible.

<!--
Please describe how you tested this change and how a reviewer could
reproduce your test, especially if this PR does not include automated
tests! If possible, please also provide terminal output and/or
screenshots demonstrating your test/reproduction.
-->

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [x] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [x] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
# Why

We should be using a consistent version of ESLint across the monorepo to
make it easier for upgrades and eventually deprecating the old
(non-flat) ESLint configurations in `eslint-config-universe`.

# How

Bumped the version of ESLint in `tools/` and migrated the ESLint
configuration file to the new flat config style.

After doing the above, fixed a handful of minor warnings that started
showing up. Additionally, disabled the `import/no-named-as-default` in
both the core and TypeScript presets as it doesn't seem to be a pattern
we use in `expo/expo`.

# Test Plan

- CI

# Checklist

- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [x] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
@pull pull Bot locked and limited conversation to collaborators May 2, 2026
@pull pull Bot added the ⤵️ pull label May 2, 2026
@pull pull Bot merged commit d3a9295 into code:main May 2, 2026
25 of 31 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants