Skip to content

Expo plugin: iOS build phases break when the project path contains a space (unquoted command substitutions) #6583

Description

@stoiqueantique

Description

withSentryIOS (Expo config plugin, @sentry/react-native 7.2.0) writes two Xcode build phases whose command substitutions are unquoted, so any project living in a path with a space fails to build (/bin/sh: /Users/me/My: No such file or directory):

  1. plugin/src/withSentryIOS.ts — "Bundle React Native code and images" rewrite:

    (match) => `/bin/sh ${SENTRY_REACT_NATIVE_XCODE_PATH} ${match}`

    SENTRY_REACT_NATIVE_XCODE_PATH is a backtick command substitution that expands to an absolute path; unquoted, it word-splits. Same for the react-native-xcode.sh invocation kept from the template.

  2. Same file — "Upload Debug Symbols to Sentry" phase:

    shellScript: `/bin/sh ${SENTRY_REACT_NATIVE_XCODE_DEBUG_FILES_PATH}`,
  3. scripts/sentry-xcode.sh — the SENTRY_DISABLE_AUTO_UPLOAD=true branch re-parses its argument unquoted:

    /bin/sh -c "$REACT_NATIVE_XCODE"

    (the upload-enabled branch already re-quotes via REACT_NATIVE_XCODE_WITH_SENTRY — only the disable branch is broken)

Reproduction (no Xcode needed)

The defect is pure shell parsing:

$ export PODS_TARGET_SRCROOT="/tmp/My App/node_modules/x/ios"
$ sh -c 'echo /bin/sh `echo "$PODS_TARGET_SRCROOT/../scripts/f.sh"`'
/bin/sh /tmp/My App/node_modules/x/ios/../scripts/f.sh

Three words after expansion → at execution time /bin/sh receives /tmp/My as its script. End-to-end: put any Expo app using the Sentry plugin in a directory whose path contains a space, npx expo prebuild -p ios, then build — the "Bundle React Native code and images" phase fails with /bin/sh: <first-path-segment>: No such file or directory.

Fix (verified on a real app in a spaced path — full Release build green with these applied via patch-package)

--- a/plugin/src/withSentryIOS.ts
+++ b/plugin/src/withSentryIOS.ts
@@
-        shellScript: `/bin/sh ${SENTRY_REACT_NATIVE_XCODE_DEBUG_FILES_PATH}`,
+        shellScript: `/bin/sh "${SENTRY_REACT_NATIVE_XCODE_DEBUG_FILES_PATH}"`,
@@
-  return script.replace(/^.*?(packager|scripts)\/react-native-xcode\.sh\s*(\\'\\\\")?/m,
-    (match) => `/bin/sh ${SENTRY_REACT_NATIVE_XCODE_PATH} ${match}`);
+  return script.replace(/^.*?(packager|scripts)\/react-native-xcode\.sh.*$/m,
+    (match) => `/bin/sh "${SENTRY_REACT_NATIVE_XCODE_PATH}" "${match}"`);

--- a/scripts/sentry-xcode.sh
+++ b/scripts/sentry-xcode.sh
@@
-  /bin/sh -c "$REACT_NATIVE_XCODE"
+  /bin/sh -c "\"$REACT_NATIVE_XCODE\""

Note on the regex change: with the Expo bare template, the old regex stops at react-native-xcode.sh, leaving the substitution's trailing '" + backtick outside the match — so wrapping only ${match} in quotes would produce an unbalanced line. Matching to end-of-line keeps the full backtick expression inside the added quotes. Quoted command substitutions ("") are valid as command words and collapse to a single argument.

Related: expo/expo has the same class of bug in the expo-constants / expo-updates podspecs and in the bare template's bundle phase — reported in expo/expo#48705.

SDK version

  • @sentry/react-native 7.2.0 (Expo plugin), Expo SDK 54, react-native 0.81.5, Xcode 26.6, macOS 26.3.1

Metadata

Metadata

Assignees

Projects

Status
Waiting for: Product Owner

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions