Repository navigation
iOS: share one Debug/Release prebuilt CONFIG resolver (Hermes / RNCore / RNDeps) — DEBUG=1 grep misses static CocoaPods Debug #59116
Description
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/ dtorfacebook::hermes::cdp::CDPAgent::create- referenced from
HermesRuntimeTargetDelegate/HermesRuntimeAgentDelegateinreacthermes
- 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 nmon the on-diskhermesvmshows noCDPDebugAPI(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)
- #41055 — stopped using
$CONFIGURATION, infer Release unlessDEBUG=1inGCC_PREPROCESSOR_DEFINITIONS(still on main / 0.88). - #57574 (
62a2b386c91985a46ee048dfa6e2216ba42a7e89) — inverse Hermes bug (Debug hermesvm stuck in Release after pod reinstall); marker moved underPods/hermes-engine/. Does not change CONFIG detection; still assumes Debug and skips extract when the marker is missing. - #57598 / #57831 — React-Core-prebuilt marker / interrupted-swap recoverability. Same CONFIG grep remains.
- Debugger flags by config type (#48174) —
utils.rbsetsHERMES_ENABLE_DEBUGGER=1on:debugforReact-hermes/hermes-engine, which can disagree with the replace script'sDEBUG=1check.
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:
- Shared CONFIG resolver — e.g.
scripts/xcode/resolve-prebuilt-configuration.sh(or a small function next towith-environment.sh) that all three script phases source. Portable signals only:$CONFIGURATION == Debugand/or$POD_CONFIGURATION_DEBUG == YES- keep
DEBUG=1as a fallback for custom flavor names (Make hermes build fetching more flavour resilient #41055 intent) - do not put
HERMES_ENABLE_DEBUGGER=1in the shared helper (Hermes-only;utils.rbcan OR that in for hermes-engine if you still want it)
- Shared marker helper in JS — extract when marker missing (don't assume Debug destroot); optionally bring Hermes / RNDeps up to RNCore's U3
in-progressrecoverability, parameterized by destroot path. - 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
react-native-bot commentedon Oct 15, 2026
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.
mikehardy commentedon Oct 15, 2026
Hey folks 👋
Happy update: here’s a minimal Reproducer Template project that fails the Debug iOS link with the same undefined
CDPDebugAPI/CDPAgentsymbols from the issue body.Repo: https://github.com/mikehardy/rn59116-debug-hermes-swap-repro
Commit:
eaf691905794fba8b3059a375e3b9c6b00dd226f—repro: force Debug static CocoaPods Hermes CDP link failureSteps
git clone https://github.com/mikehardy/rn59116-debug-hermes-swap-repro.git && cd rn59116-debug-hermes-swap-repro./scripts/repro-ios-debug.sh(or the manual
yarn/pod install/xcodebuild … -configuration Debugcommands in the README)Expected failure
Linker undefined symbols, e.g.:
On the failing tree you’ll also see
Pods/hermes-engine/.last_build_configuration==Releaseandnmon the simulatorhermesvmshows 0CDPDebugAPIsymbols — i.e. the Replace Hermes script kept/extracted Release hermesvm on a Debug build becauseGCC_PREPROCESSOR_DEFINITIONSonhermes-enginehad noDEBUG=1for the stock grep.Setup notes (crystal clear)
use_frameworks! :linkage => :staticis required for this shape.RCT_USE_PREBUILT_RNCORE=0/RCT_USE_RN_DEP=0— with default prebuiltReact.xcframeworkthe CDP refs never show up at app link; from-sourceReact-hermes(withHERMES_ENABLE_DEBUGGER=1) is what surfaces the mismatch.post_installstrips reinjectedDEBUG=1from hermes-engine Debug only so the stockDEBUG=1grep misses (matching the “def often missing on hermes-engine” case). Fresh Xcode still injectsDEBUG=1via$(inherited)from the Pods project, which otherwise masks the bug locally. No$CONFIGURATIONworkaround is applied — we want the failure.Verified on
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)