fix(core): resolve false positive loop detection when running with Bun#34640
fix(core): resolve false positive loop detection when running with Bun#34640FrozenPandaz merged 4 commits intomasterfrom
Conversation
|
@copilot disable the warning in bun since it doesn't work, we have logic somewhere to detect when running via bun |
✅ Deploy Preview for nx-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for nx-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
In Bun, async functions appear multiple times consecutively in call stacks due to how Bun handles async stack traces. The previous code used `getCallSites().slice(2)` which hardcoded the assumption that exactly 2 frames need to be skipped (preventRecursionInGraphConstruction + buildProjectGraphAndSourceMapsWithoutDaemon). In Bun, buildProjectGraphAndSourceMapsWithoutDaemon appeared twice consecutively, so after slicing 2 frames, one extra occurrence remained, triggering a false positive loop detection error. Fix: Dynamically find the first occurrence of buildProjectGraphAndSourceMapsWithoutDaemon and skip all consecutive occurrences before checking for the loop pattern. Co-authored-by: AgentEnder <6933928+AgentEnder@users.noreply.github.com>
Bun's async stack traces include extra consecutive frames for async functions that cause false positives in the call stack recursion check. Since the check doesn't work reliably in Bun, skip it entirely when running under Bun (detected via `'Bun' in globalThis`, consistent with the existing Bun detection in isolated-plugin.ts). Co-authored-by: AgentEnder <6933928+AgentEnder@users.noreply.github.com>
Done in 9b445b6. The check now returns early when Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
View your CI Pipeline Execution ↗ for commit 40faa79
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
✅ The fix from Nx Cloud was applied
We've fixed the recursion detection logic to properly handle async frame duplication across different JavaScript runtimes. The previous approach of skipping the check entirely for Bun was replaced with dynamic frame skipping that works correctly in both Node.js and Bun, preventing false positive loop detection errors while maintaining proper recursion checks in all scenarios.
Warning
❌ We could not verify this fix.
Suggested Fix changes
diff --git a/packages/nx/src/project-graph/project-graph.ts b/packages/nx/src/project-graph/project-graph.ts
index 2d0ec04a85..5e850cfdeb 100644
--- a/packages/nx/src/project-graph/project-graph.ts
+++ b/packages/nx/src/project-graph/project-graph.ts
@@ -416,16 +416,36 @@ export async function createProjectGraphAndSourceMapsAsync(
}
export function preventRecursionInGraphConstruction() {
- // Bun's async stack traces include extra frames that cause false positives in the
- // recursion check below, so we skip the check when running under Bun.
- if ('Bun' in globalThis) {
- return;
- }
+ const allFrames = getCallSites();
+
+ // Find the first occurrence of buildProjectGraphAndSourceMapsWithoutDaemon in the call stack.
+ // This represents the current invocation and should be skipped for the recursion check.
+ const firstOccurrenceIndex = allFrames.findIndex(
+ (f) =>
+ f.getFunctionName() === buildProjectGraphAndSourceMapsWithoutDaemon.name
+ );
- // preventRecursionInGraphConstruction -> callee -> ...
- // slice removes preventRecursionInGraphConstruction and its caller,
- // which is useful when using this function to detect recursion in buildProjectGraphAndSourceMapsWithoutDaemon
- const stackframes = getCallSites().slice(2);
+ let stackframes: NodeJS.CallSite[];
+
+ if (firstOccurrenceIndex !== -1) {
+ // Skip the current invocation frame and any consecutive frames with the same function name.
+ // Some runtimes (e.g. Bun) include extra async frames for the same call, which would
+ // otherwise cause a false positive loop detection.
+ let startIndex = firstOccurrenceIndex + 1;
+ while (
+ startIndex < allFrames.length &&
+ allFrames[startIndex].getFunctionName() ===
+ buildProjectGraphAndSourceMapsWithoutDaemon.name
+ ) {
+ startIndex++;
+ }
+ stackframes = allFrames.slice(startIndex);
+ } else {
+ // If buildProjectGraphAndSourceMapsWithoutDaemon is not in the stack (e.g., when called
+ // from daemon client), fall back to the original slice(2) behavior.
+ // preventRecursionInGraphConstruction -> callee -> ...
+ stackframes = allFrames.slice(2);
+ }
if (
stackframes.some((f) => {
🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.
View interactive diff ↗➡️ This fix was applied by Craigory Coppola
🎓 Learn more about Self-Healing CI on nx.dev
Co-authored-by: Copilot <Copilot@users.noreply.github.com>
#34640) Bun includes extra consecutive async frames for async functions in call stacks, causing `preventRecursionInGraphConstruction` to falsely detect a recursive loop during normal project graph construction. ## Root Cause `preventRecursionInGraphConstruction` uses `getCallSites().slice(2)` to skip the top 2 frames (itself + `buildProjectGraphAndSourceMapsWithoutDaemon`), then checks if `buildProjectGraphAndSourceMapsWithoutDaemon` appears again in the remaining frames. In Bun, `buildProjectGraphAndSourceMapsWithoutDaemon` appears **twice consecutively** due to async frame duplication — leaving one occurrence after the slice, which incorrectly triggers the loop error. **Node call stack (after `slice(2)`):** ``` #0 createProjectGraphAndSourceMapsAsync ← clean #1 createProjectGraphAsync #2 runOne ``` **Bun call stack (after `slice(2)`):** ``` #0 buildProjectGraphAndSourceMapsWithoutDaemon ← false positive! #1 createProjectGraphAndSourceMapsAsync #2 createProjectGraphAndSourceMapsAsync ← Bun duplicates async frames #3 createProjectGraphAsync ``` ## Fix Since the call stack recursion check does not work reliably under Bun, `preventRecursionInGraphConstruction` now returns early when running under Bun, detected via `'Bun' in globalThis` — consistent with the existing Bun runtime detection pattern used elsewhere in the codebase (e.g., `isolated-plugin.ts`). The original `slice(2)` logic is preserved unchanged for Node.js and other runtimes. ```ts export function preventRecursionInGraphConstruction() { // Bun's async stack traces include extra frames that cause false positives in the // recursion check below, so we skip the check when running under Bun. if ('Bun' in globalThis) { return; } // ... existing Node.js check ... } ``` <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> ---- *This section details on the original issue you should resolve* <issue_title>getCallSites output differs between Node and Bun triggering loop detection</issue_title> <issue_description>### Current Behavior Hey team, I am trying to use Bun (1.3.5) instead of Node (v22.17.0) for running Nx (v22.1.3) and bumped into the following error: Command: ```bash bunx --bun nx run api:build ``` Error: ``` NX Project graph construction cannot be performed due to a loop detected in the call stack. This can happen if 'createProjectGraphAsync' is called directly or indirectly during project graph construction. To avoid this, you can add a check against "global.NX_GRAPH_CREATION" before calling "createProjectGraphAsync". Call stack: buildProjectGraphAndSourceMapsWithoutDaemon (/app/node_modules/.pnpm/nx@22.1.3_@swc-node+register@1.10.9_@swc+core@1.11.1_@swc+helpers@0.5.15__@swc+types@0.1.18_t_twtgkxomntuzxcyp4ewkmtxn2q/node_modules/nx/src/project-graph/project-graph.js:81:62) createProjectGraphAndSourceMapsAsync (/app/node_modules/.pnpm/nx@22.1.3_@swc-node+register@1.10.9_@swc+core@1.11.1_@swc+helpers@0.5.15__@swc+types@0.1.18_t_twtgkxomntuzxcyp4ewkmtxn2q/node_modules/nx/src/project-graph/project-graph.js:274:31) createProjectGraphAndSourceMapsAsync (/app/node_modules/.pnpm/nx@22.1.3_@swc-node+register@1.10.9_@swc+core@1.11.1_@swc+helpers@0.5.15__@swc+types@0.1.18_t_twtgkxomntuzxcyp4ewkmtxn2q/node_modules/nx/src/project-graph/project-graph.js:225:53) createProjectGraphAsync (/app/node_modules/.pnpm/nx@22.1.3_@swc-node+register@1.10.9_@swc+core@1.11.1_@swc+helpers@0.5.15__@swc+types@0.1.18_t_twtgkxomntuzxcyp4ewkmtxn2q/node_modules/nx/src/project-graph/project-graph.js:222:45) createProjectGraphAsync (/app/node_modules/.pnpm/nx@22.1.3_@swc-node+register@1.10.9_@swc+core@1.11.1_@swc+helpers@0.5.15__@swc+types@0.1.18_t_twtgkxomntuzxcyp4ewkmtxn2q/node_modules/nx/src/project-graph/project-graph.js:205:40) runOne (/app/node_modules/.pnpm/nx@22.1.3_@swc-node+register@1.10.9_@swc+core@1.11.1_@swc+helpers@0.5.15__@swc+types@0.1.18_t_twtgkxomntuzxcyp4ewkmtxn2q/node_modules/nx/src/command-line/run/run-one.js:23:52) runOne (/app/node_modules/.pnpm/nx@22.1.3_@swc-node+register@1.10.9_@swc+core@1.11.1_@swc+helpers@0.5.15__@swc+types@0.1.18_t_twtgkxomntuzxcyp4ewkmtxn2q/node_modules/nx/src/command-line/run/run-one.js:16:23) Pass --verbose to see the stacktrace. ``` After some digging I found that the stack trace produced by https://github.com/nrwl/nx/blob/691bb320ce1e9cc2872e1a1b364d3fdeb9e1ad0e/packages/nx/src/utils/call-sites.ts differs between Node and Bun. When https://github.com/nrwl/nx/blob/691bb320ce1e9cc2872e1a1b364d3fdeb9e1ad0e/packages/nx/src/project-graph/project-graph.ts#L422 is run, the produced function call tracing is: Node (v22.17.0): ``` nrwl/nx#0 createProjectGraphAndSourceMapsAsync #1 createProjectGraphAsync #2 runOne #3 <anonymous> #4 <anonymous> #5 handleErrors #6 handler ``` Bun (1.3.5): ``` nrwl/nx#0 buildProjectGraphAndSourceMapsWithoutDaemon <- This entry causes Nx to detect a loop #1 createProjectGraphAndSourceMapsAsync #2 createProjectGraphAndSourceMapsAsync #3 createProjectGraphAsync #4 createProjectGraphAsync #5 runOne #6 runOne ``` This is not a Nx bug per-se, but wondering if this falls into the efforts of supporting Bun into Nx (i.e. https://nx.dev/blog/nx-19-5-adds-stackblitz-new-features-and-more#bun-and-pnpm-v9-support)? I will cross post the above into the Bun repo too for input. ### Expected Behavior Able to execute Nx commands with Bun ### GitHub Repo _No response_ ### Steps to Reproduce 1. Run bunx --bun nx run api:build ### Nx Report ```shell NX_DAEMON=true bunx --bun nx --disableNxCache --disableRemoteCache --outputStyle dynamic-legacy report 1 ✘ 16:18:00 NX Report complete - copy this into the issue template Node : 24.3.0 OS : darwin-arm64 Native Target : aarch64-macos pnpm : 9.6.0 nx : 22.1.3 @nx/js : 22.1.3 @nx/jest : 22.1.3 @nx/eslint : 22.1.3 @nx/workspace : 22.1.3 @nx/cypress : 22.1.3 @nx/devkit : 22.1.3 @nx/esbuild : 22.1.3 @nx/eslint-plugin : 22.1.3 @nx/module-federation : 22.1.3 @nx/nest : 22.1.3 @nx/next : 22.1.3 @nx/node : 22.1.3 @nx/playwright : 22.1.3 @nx/plugin : 22.1.3 @nx/react : 22.1.3 @nx/rollup : 22.1.3 @nx/storybook : 22.1.3 @nx/vite : 22.1.3 @nx/vitest : 22.1.3 @nx/web : 22.1.3 @nx/webpack : 22.1.3 @nx/docker : 22.1.3 nx-cloud : 19.1.0 @nrwl/nx-cloud : 19.1.0 typescript : 5.7.3 --------------------------------------- Registered Plugins: @nxlv/python --------------------------------------- Community plu... </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #33997 <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/nrwl/nx/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: AgentEnder <6933928+AgentEnder@users.noreply.github.com> Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com> Co-authored-by: Copilot <Copilot@users.noreply.github.com> (cherry picked from commit c89dd93)
|
This pull request has already been merged/closed. If you experience issues related to these changes, please open a new issue referencing this pull request. |
Bun includes extra consecutive async frames for async functions in call stacks, causing
preventRecursionInGraphConstructionto falsely detect a recursive loop during normal project graph construction.Root Cause
preventRecursionInGraphConstructionusesgetCallSites().slice(2)to skip the top 2 frames (itself +buildProjectGraphAndSourceMapsWithoutDaemon), then checks ifbuildProjectGraphAndSourceMapsWithoutDaemonappears again in the remaining frames.In Bun,
buildProjectGraphAndSourceMapsWithoutDaemonappears twice consecutively due to async frame duplication — leaving one occurrence after the slice, which incorrectly triggers the loop error.Node call stack (after
slice(2)):Bun call stack (after
slice(2)):Fix
Since the call stack recursion check does not work reliably under Bun,
preventRecursionInGraphConstructionnow returns early when running under Bun, detected via'Bun' in globalThis— consistent with the existing Bun runtime detection pattern used elsewhere in the codebase (e.g.,isolated-plugin.ts). The originalslice(2)logic is preserved unchanged for Node.js and other runtimes.Original prompt
This section details on the original issue you should resolve
<issue_title>getCallSites output differs between Node and Bun triggering loop detection</issue_title>
<issue_description>### Current Behavior
Hey team,
I am trying to use Bun (1.3.5) instead of Node (v22.17.0) for running Nx (v22.1.3) and bumped into the following error:
Command:
Error:
After some digging I found that the stack trace produced by https://github.com/nrwl/nx/blob/691bb320ce1e9cc2872e1a1b364d3fdeb9e1ad0e/packages/nx/src/utils/call-sites.ts differs between Node and Bun. When
nx/packages/nx/src/project-graph/project-graph.ts
Line 422 in 691bb32
Node (v22.17.0):
Bun (1.3.5):
This is not a Nx bug per-se, but wondering if this falls into the efforts of supporting Bun into Nx (i.e. https://nx.dev/blog/nx-19-5-adds-stackblitz-new-features-and-more#bun-and-pnpm-v9-support)?
I will cross post the above into the Bun repo too for input.
Expected Behavior
Able to execute Nx commands with Bun
GitHub Repo
No response
Steps to Reproduce
Nx Report