Skip to content

iOS: share one Debug/Release prebuilt CONFIG resolver (Hermes / RNCore / RNDeps) — DEBUG=1 grep misses static CocoaPods Debug #59116

Open

Description

@mikehardy

Created-by: Cursor models

Invertase / React Native Firebase: This directly affects our work supporting Firebase via SPM alongside CocoaPods (static and dynamic). We're paying a lot of attention to the Debug/Release prebuilt swap path right now. If the proposed solution design below looks right to you, please say so — we'll follow up with a PR. If you'd rather take a different shape, we'd rather hear that before we open one.


Hello all you excellent react-native maintainers 👋

TL;DR

On RN 0.88.0-rc.3, our Detox e2e app (CocoaPods + use_frameworks! :linkage => :static, Debug) fails to link with undefined facebook::hermes::cdp::CDPDebugAPI::* from HermesRuntimeTargetDelegate / reacthermes.

Root cause appears to be the Hermes replace script selecting a Release hermesvm (no CDP) while React-hermes is compiled with HERMES_ENABLE_DEBUGGER=1. The script phase still decides Debug vs Release by grepping GCC_PREPROCESSOR_DEFINITIONS for DEBUG=1 (#41055). Under static CocoaPods that def is often missing on the hermes-engine target, so CONFIG stays Release.

I think the same CONFIG stanza is copy-pasted on three prebuilt pods, and it would be neat to fix it once in a shared helper rather than three more one-offs. Looking for a sanity check on the design before we open a PR.

Symptoms

  • Link error (Debug, CocoaPods, static frameworks), undefined:
    • facebook::hermes::cdp::CDPDebugAPI::addConsoleMessage / create / dtor
    • facebook::hermes::cdp::CDPAgent::create
    • referenced from HermesRuntimeTargetDelegate / HermesRuntimeAgentDelegate in reacthermes
  • Concrete CI log (our repo, main @ a5f4f1f0b, job iOS (debug, cocoapods, 0), step Build iOS App Debug) — jumps to the undefined-symbol lines:
    https://github.com/invertase/react-native-firebase/actions/runs/38622462126/job/116053218312#step:26:8791
  • nm on the on-disk hermesvm shows no CDPDebugAPI (Release flavor) while .last_build_configuration / build log can still claim a swap ran
  • Release + CocoaPods and Debug + dynamic (in our matrix) stay green; the pain shows up when Debug React-hermes expects CDP and the destroot is Release

Prior art (related, not the same bug)

  1. #41055 — stopped using $CONFIGURATION, infer Release unless DEBUG=1 in GCC_PREPROCESSOR_DEFINITIONS (still on main / 0.88).
  2. #57574 (62a2b386c91985a46ee048dfa6e2216ba42a7e89) — inverse Hermes bug (Debug hermesvm stuck in Release after pod reinstall); marker moved under Pods/hermes-engine/. Does not change CONFIG detection; still assumes Debug and skips extract when the marker is missing.
  3. #57598 / #57831 — React-Core-prebuilt marker / interrupted-swap recoverability. Same CONFIG grep remains.
  4. Debugger flags by config type (#48174) — utils.rb sets HERMES_ENABLE_DEBUGGER=1 on :debug for React-hermes / hermes-engine, which can disagree with the replace script's DEBUG=1 check.

Where the pattern lives today

Identical shell in three podspecs (then three Node replace scripts):

Pod Script phase CONFIG Replace script
hermes-engine DEBUG=1 grep sdks/hermes-engine/utils/replace_hermes_version.js
React-Core-prebuilt same scripts/replace-rncore-version.js (U3 in-progress)
ReactNativeDependencies same third-party-podspecs/replace_dependencies_version.js (still assume-Debug-if-missing)

You already have a solid CocoaPods helper library under scripts/cocoapods/, but this Debug/Release choice is not there — it's inline shell duplicated in the podspecs.

Proposed design (feedback welcome)

In increasing order of effort / what I think is correctness:

  1. Shared CONFIG resolver — e.g. scripts/xcode/resolve-prebuilt-configuration.sh (or a small function next to with-environment.sh) that all three script phases source. Portable signals only:
    • $CONFIGURATION == Debug and/or $POD_CONFIGURATION_DEBUG == YES
    • keep DEBUG=1 as a fallback for custom flavor names (Make hermes build fetching more flavour resilient #41055 intent)
    • do not put HERMES_ENABLE_DEBUGGER=1 in the shared helper (Hermes-only; utils.rb can OR that in for hermes-engine if you still want it)
  2. Shared marker helper in JS — extract when marker missing (don't assume Debug destroot); optionally bring Hermes / RNDeps up to RNCore's U3 in-progress recoverability, parameterized by destroot path.
  3. One unit test for the resolver (+ a cheap contract that the three podspecs call it). That's the "one test we all agreed was sufficient" idea — if the helper is right, the three call sites stay thin.

Happy to implement (1)+(3) first and leave marker unification as a follow-up if that's cleaner for review.

Local workaround

We're carrying a tests-only patch-package on react-native@0.88.0-rc.3 for Hermes CONFIG + "always extract when marker missing" so our CI Debug+CocoaPods cell links again. We'd rather delete that when something like the above lands upstream.

Environment

  • react-native 0.88.0-rc.3
  • hermes-engine 260318099.0.4 (prebuilt tarballs)
  • iOS Debug, CocoaPods, use_frameworks! :linkage => :static
  • Also reproduced with Firebase SPM off for that matrix cell; Auth/SceneDelegate changes not required (fails on main tip without them)

Thanks for any feedback on whether this design is the right shape — if it is, we'll open a PR.

Cheers
-Mike

Activity

  1. react-native-bot commented on Oct 15, 2026

    @react-native-bot
    Collaborator

    Warning

    Missing reproducer: We could not detect a reproducible example in your issue report. Reproducers are mandatory and we can accept only one of those as a valid reproducer:


    You can read more about about it on our website: How to report a bug.

  2. mikehardy commented on Oct 15, 2026

    @mikehardy
    ContributorAuthor

    Hey folks 👋

    Happy update: here’s a minimal Reproducer Template project that fails the Debug iOS link with the same undefined CDPDebugAPI / CDPAgent symbols from the issue body.

    Repo: https://github.com/mikehardy/rn59116-debug-hermes-swap-repro
    Commit: eaf691905794fba8b3059a375e3b9c6b00dd226f — repro: force Debug static CocoaPods Hermes CDP link failure

    Steps

    1. git clone https://github.com/mikehardy/rn59116-debug-hermes-swap-repro.git && cd rn59116-debug-hermes-swap-repro
    2. ./scripts/repro-ios-debug.sh
      (or the manual yarn / pod install / xcodebuild … -configuration Debug commands in the README)

    Expected failure

    Linker undefined symbols, e.g.:

    "facebook::hermes::cdp::CDPDebugAPI::addConsoleMessage(...)", referenced from:
        ... HermesRuntimeTargetDelegate.o in reacthermes
    "facebook::hermes::cdp::CDPAgent::create(...)", referenced from:
        ... HermesRuntimeAgentDelegate.o in reacthermes
    ** BUILD FAILED **
    

    On the failing tree you’ll also see Pods/hermes-engine/.last_build_configuration == Release and nm on the simulator hermesvm shows 0 CDPDebugAPI symbols — i.e. the Replace Hermes script kept/extracted Release hermesvm on a Debug build because GCC_PREPROCESSOR_DEFINITIONS on hermes-engine had no DEBUG=1 for the stock grep.

    Setup notes (crystal clear)

    1. use_frameworks! :linkage => :static is required for this shape.
    2. RCT_USE_PREBUILT_RNCORE=0 / RCT_USE_RN_DEP=0 — with default prebuilt React.xcframework the CDP refs never show up at app link; from-source React-hermes (with HERMES_ENABLE_DEBUGGER=1) is what surfaces the mismatch.
    3. The Podfile’s small post_install strips reinjected DEBUG=1 from hermes-engine Debug only so the stock DEBUG=1 grep misses (matching the “def often missing on hermes-engine” case). Fresh Xcode still injects DEBUG=1 via $(inherited) from the Pods project, which otherwise masks the bug locally. No $CONFIGURATION workaround is applied — we want the failure.
    4. Template RN is 0.87.1 / hermes-engine 250829098.0.17 — same CONFIG grep as 0.88.0-rc.3.

    Verified on

    • Xcode 27.0 (27A266a)
    • iPhone 17 simulator / iOS 27
    • macOS 27.0.1

    Hope this unblocks the missing-reproducer check. Happy to tweak the force-shape if you’d rather see a different knob.

    Cheers
    -Mike

    Created-by: Cursor models (repro wiring / verify loop)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs: AttentionIssues where the author has responded to feedback.Needs: ReproThis issue could be improved with a clear list of steps to reproduce the issue.Partnerp: contributor

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions