Skip to content

fix(mobile): build react-native-worklets from source on EAS - #3996

Merged
trunk-io[bot] merged 1 commit into
mainfrom
tom/fix-ios-build-worklets-from-source
Jul 30, 2026
Merged

fix(mobile): build react-native-worklets from source on EAS#3996
trunk-io[bot] merged 1 commit into
mainfrom
tom/fix-ios-build-worklets-from-source

Conversation

@Gilbert09

@Gilbert09 Gilbert09 commented Jul 30, 2026

Copy link
Copy Markdown
Member

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 RNWorklets XCFramework.

1. Cold builds could not archive

Pods/Headers/Private/RNReanimated/reanimated/RuntimeDecorators/UIRuntimeDecorator.h:6:10
#include <worklets/Compat/StableApi.h>
         ^ '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 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

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 — this hit ad-hoc on preview and app-store on production identically, which is why it initially looked like the Xcode 16+ export-method rename.

Changes

Opt RNWorklets out of prebuilt modules via expo.autolinking.buildFromSource in apps/mobile/package.json. This is the documented opt-out in expo-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 an ios sub-key — nesting it there made fastlane resolve the archive destination as generic/platform=macOS, and xcodebuild then failed with Unable 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 source
  • no StableApi.h error
  • Archive Succeeded
  • no Unknown Distribution Error
  • Successfully exported and signed the ipa file: PostHog.ipa (24.0 MB)

This was a cold build (the package.json change invalidates the native fingerprint), so it exercises exactly the path that had been failing.

Android build 1d721015 finished green on the same commit (production AAB, build 18), confirming no regression from the shared buildFromSource entry.

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=1 is 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

  • Publish to changelog?
  • Alert Sales and Marketing teams?

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.
@trunk-io

trunk-io Bot commented Jul 30, 2026

Copy link
Copy Markdown

😎 Merged successfully - details.

@Gilbert09 Gilbert09 added the Stamphog This will request an autostamp by stamphog on small changes label Jul 30, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@talyn-app

talyn-app Bot commented Jul 30, 2026

Copy link
Copy Markdown

/trunk merge

@trunk-io
trunk-io Bot merged commit 21b3d36 into main Jul 30, 2026
33 checks passed
@trunk-io
trunk-io Bot deleted the tom/fix-ios-build-worklets-from-source branch July 30, 2026 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Stamphog This will request an autostamp by stamphog on small changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant