Skip to content

iOS: Sentry -force_load silently dropped when another pod sets OTHER_LDFLAGS[sdk=…] in user_target_xcconfig #6800

Closed

Description

@MrShadowPotato

What React Native libraries do you use?

Hermes, RN New Architecture, React Navigation, Expo (mobile only), Expo Application Services (EAS)

Are you using sentry.io or on-premise?

sentry.io (SaS)

Are you using any other error monitoring solution alongside Sentry?

No

Other Error Monitoring Solution Name

No response

@sentry/react-native SDK Version

8.28.0

How does your development environment look like?

@sentry/react-native 8.28.0 (Sentry Cocoa 9.29.0, default static linkage)
react-native 0.86.3, expo 57.0.25 (prebuild / CNG)
CocoaPods 1.17.0, Xcode 27.0
Other pod involved: react-native-executorch 0.10.4

Sentry.init()

// Not related to init; this is a build-time (pod install) issue.
Sentry.init({
  dsn: 'https://...@sentry.io/...',
});

Steps to Reproduce

  1. Use @sentry/react-native@8.28.0 (static linkage, i.e. without USE_FRAMEWORKS=dynamic) together with any other pod that sets OTHER_LDFLAGS[sdk=iphoneos*] / OTHER_LDFLAGS[sdk=iphonesimulator*] in its user_target_xcconfig. For example, react-native-executorch@0.10.4.
  2. Run pod install.
  3. Look at OTHER_LDFLAGS[sdk=…] in Pods/Target Support Files/Pods-<App>/Pods-<App>.debug.xcconfig.

Expected Result

The app target keeps -force_load ".../Sentry.xcframework/<slice>/Sentry.framework/Sentry" for each SDK, as RNSentry.podspec intends. The podspec comment explains why: without it, symbols reachable only through ObjC categories or Swift-only metadata are dead-stripped and crash at runtime with "unrecognized selector".

Actual Result

pod install warns:

[!] Can't merge user_target_xcconfig for pod targets: ["RNSentry", "react-native-executorch"]. Singular build setting OTHER_LDFLAGS[sdk=iphoneos*] has different values.
[!] Can't merge user_target_xcconfig for pod targets: ["RNSentry", "react-native-executorch"]. Singular build setting OTHER_LDFLAGS[sdk=iphonesimulator*] has different values.

and neither pod's value is written. The app still builds and runs, so the missing -force_load goes unnoticed until a stripped symbol is hit at runtime.

Cause. CocoaPods only concatenates user_target_xcconfig values across pods for keys in its PLURAL_SETTINGS list, and it matches the exact key (cocoapods/target/build_settings.rb, merged_xcconfigs). OTHER_LDFLAGS is on that list; OTHER_LDFLAGS[sdk=iphoneos*] is not. The conditional key is treated as a singular setting, and when two pods disagree CocoaPods keeps neither. Any app that pairs RNSentry 8.x with another pod using the same conditional key loses Sentry's force-load silently.

Suggested fix. Put the SDK-specific flags in a variable only RNSentry sets, and add that variable to plain OTHER_LDFLAGS, which CocoaPods does merge:

force_load_flags = sentry_slice_refs_by_sdk.each_with_object({}) do |(sdk, slice_ids), acc|
  loads = slice_ids.map do |slice|
    %(-force_load "#{File.join(sentry_xcframework_ref, slice, 'Sentry.framework', 'Sentry')}")
  end
  acc["SENTRY_FORCE_LOAD_LDFLAGS[sdk=#{sdk}*]"] = loads.join(' ')
end
force_load_flags['OTHER_LDFLAGS'] = '$(inherited) $(SENTRY_FORCE_LOAD_LDFLAGS)'

I tested the same pattern locally, applied to the other pod (react-native-executorch). The warning disappears, and xcodebuild -showBuildSettings resolves both Sentry's and the other pod's force-loads for -sdk iphoneos and -sdk iphonesimulator. The FRAMEWORK_SEARCH_PATHS[sdk=…] keys in the same podspec would hit this too if another pod ever sets them.

Workaround for anyone hitting this now: concatenate the conflicting values yourself in post_install:

installer.aggregate_targets.each do |target|
  flags = Hash.new { |h, k| h[k] = [] }
  target.pod_targets.each do |pod|
    pod.spec_consumers.each do |consumer|
      consumer.user_target_xcconfig.each do |key, value|
        flags[key] << value.sub('$(inherited)', '').strip if key.start_with?('OTHER_LDFLAGS[')
      end
    end
  end
  conflicting = flags.select { |_k, v| v.uniq.length > 1 }
  next if conflicting.empty?

  target.xcconfigs.each do |config_name, xcconfig|
    conflicting.each { |key, values| xcconfig.attributes[key] = (['$(inherited)'] + values.uniq).join(' ') }
    xcconfig.save_as(target.xcconfig_path(config_name))
  end
end

The same pattern in react-native-executorch is reported at: software-mansion/react-native-executorch#1503

Activity

  1. linear-code commented on Sep 29, 2026

    @linear-code
  2. moved this to Waiting for: Product Owner in GitHub Issues with 👀 3on Sep 29, 2026
  3. antonis commented on Sep 29, 2026

    @antonis
    Contributor

    Thank you for reporting. We will investigate this and iterate back 🙇

  4. self-assigned this
    on Sep 29, 2026
  5. moved this from Waiting for: Product Owner to No status in GitHub Issues with 👀 3on Sep 29, 2026
  6. added a commit that references this issue on Sep 30, 2026
    5920f6b
  7. github-actions commented on Oct 1, 2026

    @github-actions
    Contributor

    A PR closing this issue has just been released 🚀

    This issue was referenced by PR #6801, which was included in the 8.29.0 release.

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

Metadata

Metadata

Assignees

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions