Skip to content

iOS: intermittent "Cycle in dependencies between targets 'ReactCodegen' and '<Pod>'" on incremental builds (codegenConfig.jsSrcsDir with no Native* specs) #57760

Description

@Levin-nik

Description

On the New Architecture, an incremental iOS build fails with a dependency cycle involving ReactCodegen:

error: Cycle in dependencies between targets 'ReactCodegen' and '<SomePod>'; building could produce unreliable results.
Cycle path: ReactCodegen → <SomePod> → ReactCodegen

The failure is intermittent: a clean build passes, but every subsequent (incremental) build fails. This makes it look non-deterministic and hard to reproduce.

This commonly surfaces when building with Detox (whose config sets -derivedDataPath inside the project, e.g. ios/build), while a normal react-native run-ios (which uses Xcode's default DerivedData outside the repo) is unaffected.

Root cause

getInputFiles() in scripts/codegen/generate-artifacts-executor/generateReactCodegenPodspec.js emits a directory path for the [CP-User] Generate Specs script phase's input_files instead of an empty list, when an app's codegenConfig.jsSrcsDir contains no files matching the spec naming convention (Native*.ts/Native*.js/*NativeComponent.ts/*NativeComponent.js).

const list = String(execSync(findCommand))
  .trim()
  .split('\n')   // when `find` finds nothing → [""], not []
  .sort()
  .map(filepath => `"\${PODS_ROOT}/${path.relative(xcodeproj, filepath)}"`)
  .join(',\n');
return `[${list}]`;

When find matches no spec files, "".split('\n') yields [""], so .map runs once with filepath === "". path.relative(xcodeproj, "") resolves to a relative path to the app root (e.g. ..), so input_files becomes ["${PODS_ROOT}/.."] — a directory. That directory contains the iOS build output (e.g. ios/build). The pods producing those artifacts depend on ReactCodegen, so Xcode detects a cycle. On a clean build the output dir doesn't exist yet, so the cycle isn't triggered.

Steps to reproduce

On any New Architecture app:

  1. In package.json, set codegenConfig.jsSrcsDir to a directory that contains no Native*/*NativeComponent files (e.g. an empty specs/ folder, or one with specs that don't follow the naming convention):
    "codegenConfig": { "name": "MySpec", "type": "modules", "jsSrcsDir": "specs" }
  2. cd ios && pod install.
  3. Inspect build/generated/ios/ReactCodegen.podspec → notice 'input_files' => ["${PODS_ROOT}/.."] (a directory) instead of [].
  4. Build twice with an in-project derived data path:
    xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp \
      -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build
    • 1st build: passes.
    • 2nd (incremental) build: fails with Cycle in dependencies between targets 'ReactCodegen' and '<Pod>'.

Expected behavior

Both the clean and the incremental build succeed. With the fix, input_files is [] when no spec files match, so no directory dependency is created and there is no cycle.

Suggested fix

Filter empty entries so an empty find result yields []:

   const list = String(execSync(findCommand))
     .trim()
     .split('\n')
+    .filter(Boolean)
     .sort()

Behavior is unchanged when real Native* spec files are present (their paths are truthy and are kept).

React Native Version

0.82.1 (also reproducible on main).

Affected platforms

  • iOS
  • Android
  • macOS
  • Web

Output of npx react-native info

Not platform-tooling specific. Reproduces with Xcode 16/26 on macOS with New Architecture enabled and an in-project -derivedDataPath (e.g. via Detox).

Related PR

Fix: #57759

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.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions