From 06c7f1f2218dcddf7ede5cef97bed8196b17577f Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 23 Apr 2026 12:47:47 +0800 Subject: [PATCH 1/4] ci: reduce yarn install flakiness with concurrency cap and retries CI has been hitting intermittent failures in the "JS packages / Tests" jobs during `yarn install --inline-builds`. Failures show as: Error: EBADF: bad file descriptor, fstat on native-module packages (unrs-resolver, @pshenmic/zeromq, bufferutil, utf-8-validate). This is a yarn-berry race when many postinstall build scripts run in parallel on Azure runners. Two mitigations applied to the shared Setup Node.JS composite action: - Set YARN_CHILD_CONCURRENCY=1 to serialize native builds and eliminate the race at the cost of a small install-time regression. - Retry the install up to 3 times with backoff, as a safety net for any remaining transient issue (registry blips, network timeouts). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/nodejs/action.yaml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/actions/nodejs/action.yaml b/.github/actions/nodejs/action.yaml index 537c402977a..700fe41d110 100644 --- a/.github/actions/nodejs/action.yaml +++ b/.github/actions/nodejs/action.yaml @@ -34,4 +34,17 @@ runs: - name: Install dependencies shell: bash - run: yarn install --inline-builds + env: + # Serialize native module builds to avoid EBADF races in yarn-berry + # when many postinstall scripts run in parallel on CI runners. + YARN_CHILD_CONCURRENCY: '1' + run: | + for attempt in 1 2 3; do + if yarn install --inline-builds; then + exit 0 + fi + echo "::warning::yarn install failed on attempt ${attempt}, retrying..." + sleep $((attempt * 5)) + done + echo "::error::yarn install failed after 3 attempts" + exit 1 From 1d7b4b850e076f5a52575805b787ee96ae33bbb1 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 23 Apr 2026 12:52:29 +0800 Subject: [PATCH 2/4] ci: use correct yarn v4 env var for build-script concurrency YARN_CHILD_CONCURRENCY is a yarn classic setting and is rejected by yarn 4 as an "Unrecognized or legacy configuration setting", causing every yarn install to exit 1 immediately. The correct setting in yarn berry is taskPoolConcurrency (env var YARN_TASK_POOL_CONCURRENCY). Default is 10; use 2 to cap the race window on native builds while keeping some parallelism for speed. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/nodejs/action.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/actions/nodejs/action.yaml b/.github/actions/nodejs/action.yaml index 700fe41d110..2af3a63f300 100644 --- a/.github/actions/nodejs/action.yaml +++ b/.github/actions/nodejs/action.yaml @@ -35,9 +35,10 @@ runs: - name: Install dependencies shell: bash env: - # Serialize native module builds to avoid EBADF races in yarn-berry - # when many postinstall scripts run in parallel on CI runners. - YARN_CHILD_CONCURRENCY: '1' + # Cap concurrent build-script execution to avoid EBADF races when + # many native-module postinstall scripts run in parallel on CI. + # Default is 10; 2 keeps some parallelism for speed. + YARN_TASK_POOL_CONCURRENCY: '2' run: | for attempt in 1 2 3; do if yarn install --inline-builds; then From cb9fba04fc5009f37f652820a7549d2b5ded4961 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 23 Apr 2026 13:11:35 +0800 Subject: [PATCH 3/4] ci: disable libuv io_uring and drop --inline-builds for yarn install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Post-merge investigation showed the job failure is not a race but a deterministic regression introduced by the ubuntu-24.04 runner image 2026-04-20.95. The same PR with identical yarn state passes on the earlier image 2026-04-13.86 and fails on 2026-04-20.95 every run. Failures manifest as `EBADF: bad file descriptor, fstat` in yarn postinstall scripts for native packages (unrs-resolver, bufferutil, utf-8-validate, @pshenmic/zeromq) — all failing within <200ms of starting. This matches a known Node-level io_uring fs path interaction with piped stdio under high postinstall concurrency. Two targeted changes: - UV_USE_IO_URING=0 tells libuv to skip io_uring and use the threadpool fs backend, which doesn't exhibit the EBADF regression. - Drop `--inline-builds`; the default mode writes per-package build logs instead of piping every postinstall's stdio through yarn, removing the stdio-pipe pressure that compounds the EBADF issue. On failure yarn still reports the log paths. Replaces the earlier YARN_TASK_POOL_CONCURRENCY setting, which CI logs confirmed does not gate postinstall script concurrency. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/nodejs/action.yaml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/actions/nodejs/action.yaml b/.github/actions/nodejs/action.yaml index 2af3a63f300..a502bd6565a 100644 --- a/.github/actions/nodejs/action.yaml +++ b/.github/actions/nodejs/action.yaml @@ -35,13 +35,17 @@ runs: - name: Install dependencies shell: bash env: - # Cap concurrent build-script execution to avoid EBADF races when - # many native-module postinstall scripts run in parallel on CI. - # Default is 10; 2 keeps some parallelism for speed. - YARN_TASK_POOL_CONCURRENCY: '2' + # Recent ubuntu-24.04 runner images (2026-04-20+) surface + # intermittent `EBADF: bad file descriptor, fstat` errors from + # yarn postinstall scripts. Disabling libuv's io_uring fs backend + # reverts to the threadpool path and avoids the regression. + UV_USE_IO_URING: '0' run: | for attempt in 1 2 3; do - if yarn install --inline-builds; then + # --inline-builds piped every postinstall's stdio through yarn, + # compounding the EBADF issue. Default mode writes per-package + # build logs; on failure yarn prints the log paths. + if yarn install; then exit 0 fi echo "::warning::yarn install failed on attempt ${attempt}, retrying..." From 7032b84666a400c99914dd5c5af9a7ff709aa27e Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 23 Apr 2026 13:28:39 +0800 Subject: [PATCH 4/4] ci: pin Node to 24.14.1 to avoid EBADF regression in 24.15.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous commit blamed the ubuntu-24.04 runner image bump and tried UV_USE_IO_URING=0 + dropping --inline-builds. That was the wrong diagnosis. Full stack trace from a failing tests job shows the EBADF comes from Node's own ESM loader, not libuv fs/io_uring or yarn stdio piping: const stats = binding.fstat(fd, false, undefined, true); at tryStatSync (node:fs:391:25) at readFileSync (node:fs:447:17) at getSourceSync (node:internal/modules/esm/load:37:14) at createCJSModuleWrap (node:internal/modules/esm/translators:210:32) at ModuleLoader.commonjsStrategy (...:349:10) Error: EBADF: bad file descriptor, fstat Node.js v24.15.0 Build JS (passing) ran on runner image 20260413.86 with Node 24.14.1. Tests (failing) ran on runner image 20260420.95 with Node 24.15.0. Node 24.15.0 shipped a regression in the ESM loader's CJS translation path that raises EBADF in getSourceSync -> readFileSync during postinstall scripts that load CJS via ESM. Pin node-version to 24.14.1. Keep the retry wrapper — cheap safety net for unrelated transient install issues. Revert UV_USE_IO_URING=0 and restore --inline-builds; neither was contributing to the fix, and --inline-builds is useful for seeing postinstall output in CI logs. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/nodejs/action.yaml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/.github/actions/nodejs/action.yaml b/.github/actions/nodejs/action.yaml index a502bd6565a..ef62f30be32 100644 --- a/.github/actions/nodejs/action.yaml +++ b/.github/actions/nodejs/action.yaml @@ -5,7 +5,12 @@ inputs: node-version: description: "Node.js version to use" required: false - default: "24" + # Pinned: Node 24.15.0 shipped a regression in the ESM loader's + # CJS translation path that raises `EBADF: bad file descriptor, + # fstat` in getSourceSync -> readFileSync during postinstall + # scripts of native packages (bufferutil, utf-8-validate, + # unrs-resolver, @pshenmic/zeromq). 24.14.1 is unaffected. + default: "24.14.1" runs: using: composite steps: @@ -34,18 +39,9 @@ runs: - name: Install dependencies shell: bash - env: - # Recent ubuntu-24.04 runner images (2026-04-20+) surface - # intermittent `EBADF: bad file descriptor, fstat` errors from - # yarn postinstall scripts. Disabling libuv's io_uring fs backend - # reverts to the threadpool path and avoids the regression. - UV_USE_IO_URING: '0' run: | for attempt in 1 2 3; do - # --inline-builds piped every postinstall's stdio through yarn, - # compounding the EBADF issue. Default mode writes per-package - # build logs; on failure yarn prints the log paths. - if yarn install; then + if yarn install --inline-builds; then exit 0 fi echo "::warning::yarn install failed on attempt ${attempt}, retrying..."