Skip to content

[iOS] Fix Hermes Debug builds after installing another pod - #59159

Open
fairysword wants to merge 1 commit into
react:mainfrom
fairysword:fix/hermes-configuration-marker
Open

fairysword wants to merge 1 commit into
react:mainfrom
fairysword:fix/hermes-configuration-marker

Conversation

@fairysword

Copy link
Copy Markdown

Summary:

A successful Release build followed by adding a new CocoaPods dependency can cause the next Debug build to fail with undefined Hermes debugger symbols, including CDPDebugAPI::create and CDPAgent::create.

The Release build installs the Release Hermes binary and records Release in Pods/hermes-engine/.last_build_configuration. When a subsequent pod install installs another pod, CocoaPods cleans the existing Hermes pod. The Pre-built subspec does not preserve the configuration marker, so the marker is removed while the Release framework remains on disk.

On the next Debug build, replace_hermes_version.js receives the correct Debug configuration but assumes that a missing marker means Hermes is already Debug. It skips replacement, leaving Debug React Native code to link against a Release Hermes binary that does not export the required debugger symbols.

This change:

  • Adds .last_build_configuration to the Pre-built subspec's preserve_paths, so installing another pod does not discard the configuration recorded for the existing Hermes binary.
  • Removes the assumption that a missing marker implies Debug. If the marker is absent, the replacement script extracts the requested configuration and writes its marker.
  • Adds regression coverage for missing markers, configuration switches, fresh installations, and the existing matching-configuration fast path.

A matching marker still skips extraction. A fresh Debug installation without a marker now performs one initial extraction; subsequent builds with the same recorded configuration continue to skip it.

Changelog:

[IOS] [FIXED] - Fix Hermes Debug link failures after installing another pod following a Release build.

Test Plan:

Environment

The failure was reproduced in RNTester on the unmodified main baseline 27e77216ef2557ccbf31a01067a44dc1dd2ec846, using:

  • Xcode 27.0 (27A266a), iPhoneOS SDK, arm64.
  • Ruby 3.3.11, Bundler 2.5.22, CocoaPods 1.15.2.
  • Node.js 22.22.3, prebuilt Hermes 260318099.0.2, and hermes-compiler 260318099.0.4 selected by the repository's installation hook.
  • Default RNTester CocoaPods integration, with USE_FRAMEWORKS unset.

Both the unmodified and patched versions were tested through complete Debug → Release → add a pod → Debug sequences. Each sequence used the same workspace and DerivedData directory throughout; the patched sequence started with fresh Pods and a separate empty DerivedData directory. JavaScript bundling was enabled. Signing was disabled with CODE_SIGNING_ALLOWED=NO; the app was not launched on a device.

Reproduction on the unmodified baseline

  1. Start with a fresh checkout of the baseline above, without Masonry in RNTester's Podfile. Prepare the repository and RNTester dependencies from the repository root:

    yarn install
    yarn --cwd packages/react-native-codegen build
    cd packages/rn-tester
    bundle install
    bundle exec pod update hermes-engine --no-repo-update

    On this specific baseline, the checked-in RNTester lockfile references an older Hermes version than the local podspec. The final command aligns that dependency once, matching the repository's RNTester CI setup. This is environment preparation; the dependency installation that triggers the bug below uses ordinary pod install.

  2. Define the build command in the same terminal. The directory is inside RNTester and is shared by Debug and Release:

    REPRO_DIR="$PWD/build/hermes-marker-repro-$(date +%Y%m%d-%H%M%S)"
    mkdir -p "$REPRO_DIR"
    
    build_rntester() {
      xcodebuild \
        -workspace RNTesterPods.xcworkspace \
        -scheme RNTester \
        -configuration "$1" \
        -sdk iphoneos \
        -destination 'generic/platform=iOS' \
        -derivedDataPath "$REPRO_DIR/DerivedData" \
        -jobs 6 \
        CODE_SIGNING_ALLOWED=NO \
        build
    }
  3. Build Debug to establish a working baseline:

    build_rntester Debug

    Result: BUILD SUCCEEDED.

  4. Build Release to simulate a release build:

    build_rntester Release

    Result: BUILD SUCCEEDED. The normal Xcode build phase switches Hermes to Release and writes the Release marker.

  5. Add a new third-party dependency inside the existing RNTester target in packages/rn-tester/Podfile:

    target 'RNTester' do
      pods('RNTester')
      pod 'Masonry', '1.1.0'
    end

    Masonry is a published pod used only to trigger a normal new-pod installation in this reproduction. You can replace it with any other compatible pod that is not already installed in the project.

  6. Install the dependency, retaining the existing Pods and DerivedData directories:

    bundle exec pod install

    Result: installation succeeds and reports Installing Masonry (1.1.0). The Hermes configuration marker is now absent, but the installed arm64 Hermes binary still matches the Release archive.

  7. Build Debug again:

    build_rntester Debug

    Result: BUILD FAILED, with xcodebuild exiting with code 65. The failing link is RNTester's RNTester.debug.dylib; unresolved references originate from libReact-hermes.a.

    The Hermes build phase reports:

    No previous build detected, but Debug Configuration. No need to replace Hermes engine
    

    The linker reports undefined arm64 symbols, including:

    facebook::hermes::cdp::CDPDebugAPI::create(...)
    facebook::hermes::cdp::CDPAgent::create(...)
    clang++: error: linker command failed with exit code 1
    

Recorded results before the fix

Step Result Hermes marker Installed Hermes binary
Initial Debug build Passed Absent Debug
Release build Passed Release Release
Add Masonry and run pod install Passed Absent Release
Next Debug build Link failed, exit 65 Absent Release

The installed arm64 binary's SHA-256 was unchanged across the Masonry installation and matched the Release archive. This confirms that the marker was lost while the Release binary remained installed.

Recorded results with the patch applied

With the patch applied before the initial pod installation, the same sequence passed with fresh Pods and a separate empty DerivedData directory.

Step Result Hermes marker Installed Hermes binary
Initial Debug build Passed Debug Debug
Release build Passed Release Release
Add Masonry and run pod install Passed Release Release
Next Debug build Passed Debug Debug

The Masonry installation reported Installing Masonry (1.1.0) and retained both the Release marker and the Release binary. The next Debug build found the marker, extracted Debug Hermes, wrote Debug, and completed successfully. Binary hashes matched the respective Debug and Release archives at every step.

  • Full RNTester Debug → Release → add Masonry → Debug sequence passed with the patch applied before the initial pod installation.

  • All seven regression cases passed. From the repository root:

    yarn test packages/react-native/scripts/__tests__/replace-hermes-version-test.js --runInBand

    The cases cover missing-marker recovery in both directions, matching-marker skips without requiring an archive, normal Debug/Release switches, and recording Debug after a fresh installation without a marker.

@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 Oct 17, 2026
@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 Oct 17, 2026

@cipolleschi cipolleschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for fixing this!

@meta-codesync

meta-codesync Bot commented Oct 17, 2026

Copy link
Copy Markdown

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this in D125847848.

This branch has not been deployed

No deployments
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.

2 participants