Skip to content

Make the PowerShell path lookup lazy again, to stop spamming errors on macOS when react-native.config.js is loaded - #16430

Merged
Andrew Coates (acoates-ms) merged 6 commits into
microsoft:mainfrom
shirakaba:codex/defer-powershell-discovery
Sep 8, 2026
Merged

Make the PowerShell path lookup lazy again, to stop spamming errors on macOS when react-native.config.js is loaded#16430
Andrew Coates (acoates-ms) merged 6 commits into
microsoft:mainfrom
shirakaba:codex/defer-powershell-discovery

Conversation

@shirakaba

@shirakaba Jamie Birch (shirakaba) commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

I didn't file an issue for this yet, so I'll begin by introducing the problem before describing the solution that this PR implements.

Problem

When you try to run rnc-cli start on a Mac, in a project with react-native-windows installed, using @rnx-kit/metro-config as your Metro config, it eagerly looks for .NET and Powershell, naturally fails to find them, and makes a lot of noise:

./node_modules/.bin/rnc-cli start 
/bin/sh: dotnet.exe: command not found
/bin/sh: where: command not found
/bin/sh: dotnet.exe: command not found
/bin/sh: where: command not found
/Users/jamie/Documents/git/expo-desktop/packages/expo-desktop/MyApp4/node_modules/react-native-windows/react-native.config.js: /bin/sh: dotnet.exe: command not found
/bin/sh: where: command not found
/Users/jamie/Documents/git/expo-desktop/packages/expo-desktop/MyApp4/node_modules/@react-native-windows/find-dotnet-tools/lib-commonjs/findDotnetTools.js:62
    throw new Error('Unable to find pwsh.exe. It should have been made available by `yarn install`.');
          ^

Error: Unable to find pwsh.exe. It should have been made available by `yarn install`.
    at findPowerShell (/Users/jamie/Documents/git/expo-desktop/packages/expo-desktop/MyApp4/node_modules/@react-native-windows/find-dotnet-tools/lib-commonjs/findDotnetTools.js:62:11)
    at Object.<anonymous> (/Users/jamie/Documents/git/expo-desktop/packages/expo-desktop/MyApp4/node_modules/@react-native-windows/cli/lib-commonjs/utils/commandWithProgress.js:45:59)
    at Module._compile (node:internal/modules/cjs/loader:1812:14)
    at Object..js (node:internal/modules/cjs/loader:1943:10)
    at Module.load (node:internal/modules/cjs/loader:1533:32)
    at Module._load (node:internal/modules/cjs/loader:1335:12)
    at wrapModuleLoad (node:internal/modules/cjs/loader:255:19)
    at Module.require (node:internal/modules/cjs/loader:1556:12)
    at require (node:internal/modules/helpers:152:16)
    at Object.<anonymous> (/Users/jamie/Documents/git/expo-desktop/packages/expo-desktop/MyApp4/node_modules/@react-native-windows/cli/lib-commonjs/commands/autolinkWindows/autolinkWindows.js:47:31)

Node.js v24.14.0
● Validation Warning:

  Unknown option "watcher.unstable_workerThreads" with value false was found.
  This is probably a typing mistake. Fixing it will remove this message.


Welcome to React Native v0.81
Starting dev server on http://localhost:8081

This eager search happens when @rnx-kit/metro-config discovers platforms from the dependencies at packages/tools-react-native/src/context.ts#L147. It ends up evaluating react-native-windows/react-native.config.js in a child process, which calls findPowerShell().

(CC Tommy Nguyen (@tido64) – I think it's reasonable to fix it in react-native-windows, but tagging just in case it'd be preferable to fix it on the rnx-kit side).

History

There was no such problem in react-native-windows@0.81.27. The regression was introduced when #16075 was backported to 0.81.28 in #16235, with changes such as this one, to line 50 of packages/@react-native-windows/cli/src/utils/commandWithProgress.ts:

- export const powershell = `${process.env.SystemRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
+ const powershell = findPowerShell();

Expressions that used to just be static strings became eager file system lookups, causing side-effects on non-Windows machines.

Solution

This PR makes it so that we resolve and cache the path to PowerShell only when actually about to execute a PowerShell command. i.e. it changes it from eager to lazy.

Reproduction

Repro repository, based on Expo Desktop:

# (1) Set up the project:
git clone --branch codex/reproduce https://github.com/shirakaba/rnw-powershell-discovery-repro.git
cd rnw-powershell-discovery-repro
bun install --frozen-lockfile

# (2a) Trigger `expo start` (equivalent to `rnc-cli start`):
node --run start

# (2b) Or, more minimally, just import the metro config:
node -e "require('./metro.config.js')"

# (3) Compare two branches (run `bun install` post-checkout):
# - main: problem reproduces.
# - codex/fix: problem fixed, by patching RNW.

In my case, I used Node.js 24 and Bun 1.3.11.

See the codex/fix branch, which applies this proposed change as a patch.

Backporting

While this PR is opened against main, please consider backporting to 0.81-stable so that I can release Expo Desktop in best condition.

Testing

Astra initially wrote a whole test suite for this. I think they're a bit over the top, though, so I've pushed an extra commit to drop them. If the reviewer wants, I can restore them just by reverting that commit.

  • 10 Jest tests pass: import/serialization on simulated macOS and Windows, deferred lookup, successful caching, retry after failure, and diagnostic/repair execution. The import regressions fail against the original source on both simulated hosts.
  • CLI TypeScript build and ESLint pass; changed files formatted with Prettier.
  • Repro CI on real macOS and Windows: baseline confirms the bug; patched branch passes. Checks forbid PowerShell discovery, require empty stderr after patching, and verify both platforms remain available.

Changelog

Yes: Defer PowerShell discovery until a command or health check needs it, allowing CLI configuration to load without Windows build tools.

Microsoft Reviewers: Open in CodeFlow

@azure-pipelines

Copy link
Copy Markdown
Contributor
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@shirakaba
Jamie Birch (shirakaba) marked this pull request as ready for review September 5, 2026 16:20
@shirakaba
Jamie Birch (shirakaba) requested a review from a team as a code owner September 5, 2026 16:20
Copilot AI balanced review requested due to automatic review settings September 5, 2026 16:20
@azure-pipelines

Copy link
Copy Markdown
Contributor
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@shirakaba Jamie Birch (shirakaba) changed the title Defer PowerShell discovery until command or health check execution Make the Metro config's PowerShell path lookup lazy again, to stop spamming errors on macOS Sep 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

PowerShell lookup failures bypass existing command and health-check error handling.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Defers PowerShell discovery to avoid failures when loading RNW configuration without Windows tooling.

Changes:

  • Lazily caches PowerShell paths during command and health-check execution.
  • Adds a prerelease change record.
File summaries
File Description
commandWithProgress.ts Defers PowerShell discovery until command execution.
healthChecks.ts Defers discovery until diagnostics or repair.
Change JSON Records the CLI prerelease change.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/@react-native-windows/cli/src/commands/healthCheck/healthChecks.ts Outdated
Comment thread packages/@react-native-windows/cli/src/utils/commandWithProgress.ts
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 5, 2026 16:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

PowerShell discovery failures lose actionable diagnostics or bypass health-check failure handling.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

packages/@react-native-windows/cli/src/commands/healthCheck/healthChecks.ts:82

  • findPowerShell() can throw when the tool is absent, but this lookup occurs before the callback's failure-handling try. In that case runAutomaticFix rejects without calling loader.fail() or reporting manual-installation guidance, unlike all execa failures below. Include discovery in the handled path and fail the loader cleanly when no executable can be found.
            powershell ??= findPowerShell();
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread packages/@react-native-windows/cli/src/utils/commandWithProgress.ts Outdated
Copilot AI review requested due to automatic review settings September 6, 2026 00:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The lazy lookup is correctly implemented, with only a minor diagnostic capitalization cleanup remaining.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread packages/@react-native-windows/cli/src/commands/healthCheck/healthChecks.ts Outdated
Copilot AI review requested due to automatic review settings September 6, 2026 00:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

PowerShell lookup failures bypass the operation-specific error handling, and the health-check block needs formatting corrections.

Review details

Suppressed comments (2)

packages/@react-native-windows/cli/src/utils/commandWithProgress.ts:61

  • Keep the lazy lookup inside the existing try. As written, a missing PowerShell executable throws before this function converts failures to the supplied CodedError category, so callers report an Unknown result/exit code instead of the operation-specific failure (for example, EnableDevModeFailure).
  powershell ??= findPowerShell();

packages/@react-native-windows/cli/src/commands/healthCheck/healthChecks.ts:89

  • This newly added block does not follow the repository's TypeScript formatting rules: it uses double quotes, omits the trailing property comma, and is not wrapped as Prettier would format it. It also spells the product name as “Powershell” rather than “PowerShell”; please format this before merging so the formatting check passes.
              const errorMessage = error instanceof Error ? error.message : undefined;
              logManualInstallation({
                healthcheck: `react-native-windows dependency "${id}"`,
                message: `Error finding PowerShell${errorMessage ? `: ${errorMessage}` : ""}`
              });
  • Files reviewed: 3/3 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings September 6, 2026 00:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

PowerShell lookup bypasses coded-error handling, and the health-check addition fails repository formatting conventions.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

packages/@react-native-windows/cli/src/commands/healthCheck/healthChecks.ts:89

  • This added block does not match the repository's required Prettier style: it uses double quotes, omits the trailing comma, and leaves expressions unwrapped. As written, the formatting check will reject the file.

packages/@react-native-windows/cli/src/utils/commandWithProgress.ts:61

  • findPowerShell() can throw when PowerShell is unavailable, but this lookup is outside the function's try. That failure therefore escapes as a plain Error instead of the requested CodedError, bypassing the caller's error category and the function's existing failure handling. Include the lazy lookup in the try block.
  powershell ??= findPowerShell();
  • Files reviewed: 3/3 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@shirakaba Jamie Birch (shirakaba) changed the title Make the Metro config's PowerShell path lookup lazy again, to stop spamming errors on macOS Make the PowerShell path lookup lazy again, to stop spamming errors on macOS when react-native.config.js is loaded Sep 6, 2026
@acoates-ms

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Contributor
Azure Pipelines:
Successfully started running 1 pipeline(s).

@acoates-ms
Andrew Coates (acoates-ms) merged commit 610892c into microsoft:main Sep 8, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants