fix(mobile): build react-native-worklets from source on EAS - #3996
Merged
Conversation
iOS builds have been failing since the last green build in May, in two
different ways that turn out to share one cause: Expo's precompiled
RNWorklets XCFramework.
1. Cold builds could not archive:
Pods/Headers/Private/RNReanimated/.../UIRuntimeDecorator.h:6:10
'worklets/Compat/StableApi.h' file not found
** ARCHIVE FAILED **
RNReanimated compiles from source, but RNWorklets was consumed as a
precompiled XCFramework whose headers are only put in place by the
"[Expo] Switch RNWorklets XCFramework for build configuration" script
phase. That phase is not an input dependency of RNReanimated's
compile, so the two race and reanimated loses. Warm builds hid it by
restoring a Pods cache that already had the headers, so any change to
the native fingerprint failed while an unchanged rebuild passed.
2. Builds that did archive could not be exported:
IDEDistributionMethodManagerErrorDomain Code=2 "Unknown Distribution Error"
error: exportArchive exportOptionsPlist error for key "method"
expected one {} but found app-store
Xcode could not enumerate any distribution method, so the allowed set
was empty and every value was rejected -- ad-hoc and app-store alike.
Opting RNWorklets out of prebuilt modules fixes both: the archive builds
cleanly and exports to a signed .ipa.
EAS sets EXPO_USE_PRECOMPILED_MODULES=1 itself, and which modules
actually resolve to a prebuilt artifact varies per build (several 404 out
of the artifact cache and silently fall back to source), which is why
this looked intermittent rather than broken.
|
😎 Merged successfully - details. |
There was a problem hiding this comment.
Trivial 5-line config addition opting one native module out of prebuilt XCFramework linking on EAS; no scripts/lifecycle hooks touched, no new dependencies, tested against real builds per description.
- Author wrote 0% of the modified lines and has 59 merged PRs in these paths (familiarity MODERATE).
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✓ | no deny categories matched |
| size | ✓ | 5L, 1F substantive — within ceiling |
| tier | ✓ | T1-agent / T1a-trivial (5L, 1F, single-area, fix) |
| stamphog 2.0.0b3 | .stamphog/policy.yml @ 919a329 · reviewed head 35965df |
|
/trunk merge |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
iOS EAS builds have been broken since the last green build on 2026-05-22. They failed in two different ways, which turned out to share a single cause: Expo's precompiled
RNWorkletsXCFramework.1. Cold builds could not archive
RNReanimatedcompiles from source, butRNWorkletswas consumed as a precompiled XCFramework whose headers are only put in place by the[Expo] Switch RNWorklets XCFramework for build configurationscript phase. That phase is not an input dependency of RNReanimated's compile, so the two race and reanimated loses.Warm builds hid this by restoring a Pods cache that already contained the headers. That is why an unchanged rebuild passed while any change to the native fingerprint (
app.json,package.json) failed every time — which also made iOS effectively untestable, since no native-affecting change could get far enough to be evaluated.2. Builds that did archive could not be exported
Xcode could not enumerate any distribution method, so the allowed set was empty (
{}) and every value was rejected — this hitad-hoconpreviewandapp-storeonproductionidentically, which is why it initially looked like the Xcode 16+ export-method rename.Changes
Opt
RNWorkletsout of prebuilt modules viaexpo.autolinking.buildFromSourceinapps/mobile/package.json. This is the documented opt-out inexpo-modules-autolinking: "A list of package name patterns to opt out of prebuilt modules."Note it must live at the top level of
autolinking, not under aniossub-key — nesting it there made fastlane resolve the archive destination asgeneric/platform=macOS, and xcodebuild then failed withUnable to find a destination matching the provided destination specifier.How did you test this?
Ran production-profile EAS builds off this change (dispatched with
wait=false, so nothing was submitted to TestFlight or Play).iOS build
f47c1345— verified from the raw build log:RNWorklets ... building from sourceStableApi.herrorArchive SucceededUnknown Distribution ErrorSuccessfully exported and signed the ipa file: PostHog.ipa (24.0 MB)This was a cold build (the
package.jsonchange invalidates the native fingerprint), so it exercises exactly the path that had been failing.Android build
1d721015finished green on the same commit (production AAB, build 18), confirming no regression from the sharedbuildFromSourceentry.Trade-off worth flagging: building worklets from source instead of pulling the prebuilt XCFramework makes both platforms noticeably slower (this Android run took ~40 minutes). Correctness first — but if build times matter, the alternative is to chase a fix for the header-ordering race upstream in Expo rather than opting out.
Context
EXPO_USE_PRECOMPILED_MODULES=1is set by EAS itself, and which modules actually resolve to a prebuilt artifact varies per build — several (RNReanimated,RNScreens,RNSVG,RNCAsyncStorage) 404 out of the artifact cache and silently fall back to source. That per-build variation is why this presented as intermittent rather than consistently broken, and it is worth knowing if similar header-not-found failures appear for other precompiled modules.Automatic notifications