You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[BUG] npm exec fails with ECOMPROMISED with Node 24 on Windows #8710
I have a Github action that runs npx --yes <package> and the command fails with ECOMPROMISED when run with Node 24 on Windows. The same command works on Ubuntu and also works on Windows with Node 22.
Expected Behavior
npx --yes <package> should not fail with ECOMPROMISED error
See that npx --yes jest --version fails in job that uses Node 24 on Windows
Environment
npm: 11.6.2
Node.js: 24.10.0
OS Name: Windows Server 2025 10.0.26100 Datacenter
System Model Name: Github runner image: windows-2025 20251021.67.1
npm config:
; "global" config from C:\npm\prefix\etc\npmrc
cache = "C:\npm\cache"
; "env" config from environment
prefix = "C:\npm\prefix"
; node bin location = C:\hostedtoolcache\windows\node\24.10.0\x64\node.exe
; node version = v24.10.0
; npm local prefix = D:\a\npx-error\npx-error
; npm version = 11.6.2
; cwd = D:\a\npx-error\npx-error
; HOME = C:\Users\runneradmin
; Run npm config ls -l to show all defaults.
I'm getting the same issue when running npx with the @asyncapi/cli package. It does work after a couple tries, but the majority of the time I get this ECOMPROMISED error. The log shows this stack trace:
192 verbose stack Error: Lock compromised
192 verbose stack at AbortSignal.<anonymous> (C:\Program Files\nodejs\node_modules\npm\node_modules\libnpmexec\lib\with-lock.js:54:30)
192 verbose stack at [nodejs.internal.kHybridDispatch] (node:internal/event_target:845:20)
192 verbose stack at AbortSignal.dispatchEvent (node:internal/event_target:778:26)
192 verbose stack at runAbort (node:internal/abort_controller:488:10)
192 verbose stack at abortSignal (node:internal/abort_controller:459:3)
192 verbose stack at AbortController.abort (node:internal/abort_controller:507:5)
192 verbose stack at Timeout.touchLock (C:\Program Files\nodejs\node_modules\npm\node_modules\libnpmexec\lib\with-lock.js:176:18)
193 error code ECOMPROMISED
194 error Lock compromised
To debug I added some console.logs to the with-lock.js file:
async function maintainLock (lockPath) {
console.log("maintainLock for ",lockPath);
const controller = new AbortController()
const stats = await fs.stat(lockPath)
// fs.utimes operates on floating points seconds (directly, or via strings/Date objects), which may not match the underlying filesystem's mtime precision, meaning that we might read a slightly different mtime than we write. always round to the nearest second, since all filesystems support at least second precision
let mtime = Math.round(stats.mtimeMs / 1000)
const signal = controller.signal
async function touchLock () {
console.log("touchLock for ",lockPath);
try {
const ts = Date.now();
console.log("fs.stat for ",lockPath);
const currentStats = (await fs.stat(lockPath))
console.log("fs.stat completed for ",lockPath, ". Took ", Date.now() - ts, "ms");
const currentMtime = Math.round(currentStats.mtimeMs / 1000)
if (currentStats.ino !== stats.ino || currentMtime !== mtime) {
console.log("expected mtime: ", mtime, " current mtime: ", currentMtime);
throw new Error('Lock compromised')
}
mtime = Math.round(Date.now() / 1000)
// touch the lock, unless we just released it during this iteration
if (currentLocks.has(lockPath)) {
console.log("updating mtime for ",lockPath, " to ", mtime);
await fs.utimes(lockPath, mtime, mtime)
}
} catch (err) {
// stats mismatch or other fs error means the lock was compromised
console.log("aborting...");
controller.abort()
}
}
const timeout = setInterval(touchLock, touchInterval)
// timeout.unref()
function cleanup () {
clearInterval(timeout)
deleteLock(lockPath)
}
currentLocks.set(lockPath, cleanup)
return signal
}
This gives the following output:
⠼maintainLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠧touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠇fs.stat completed for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock . Took 52 ms
updating mtime for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock to 1763134096
⠏touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat completed for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock . Took 6 ms
updating mtime for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock to 1763134098
⠦touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat completed for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock . Took 1 ms
updating mtime for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock to 1763134100
⠸maintainLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠇touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat completed for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock . Took 2 ms
updating mtime for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock to 1763134103
⠏touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠸fs.stat completed for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock . Took 510 ms
updating mtime for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock to 1763134105
⠼touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠸touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠹touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠼touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠼touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠦touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠴touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠼touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠹touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠦touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠼touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠹touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠸touchLock for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
fs.stat for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock
⠴fs.stat completed for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock . Took 25809 ms
updating mtime for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock to 1763134132
fs.stat completed for C:\Users\patrick.ledew\AppData\Local\npm-cache\_npx\0929aae77d023606\concurrency.lock . Took 23778 ms
expected mtime: 1763134132 current mtime: 1763134105
aborting...
aborting...
aborting...
aborting...
⠦aborting...
aborting...
aborting...
aborting...
aborting...
aborting...
aborting...
aborting...
aborting...
npm error code ECOMPROMISED
npm error Lock compromised
npm error A complete log of this run can be found in: C:\Users\patrick.ledew\AppData\Local\npm-cache\_logs\2025-11-14T15_28_12_159Z-debug-0.log
Seems like the fs.stat call works fine the first couple calls, but then balloons to 500ms then 23 seconds! While this is hanging, the interval for touchLock keeps getting called. When the next fs.stat call eventually succeeds it seems to use an old value for the current mtime which triggers the abort.
For context, this is running on a Windows 365 VM so I'm unsure if that would affect fs.stat performance.
Thanks for the ping and the detailed investigation @patrickledew! I'll take a stab at fixing this. The 23 second stat calls are definitely surprising 😱
Given that it only happens in windows + node 24, it sounds like there may be a bug in node that's causing the long stat calls. But hopefully we can protect against that (e.g. perhaps a higher staleThreshold, and change from setInterval -> setTimeout)
Looks like it's not specific to node 24, it happens in node 22 as well. The test.yml workflow in the PR description is actually using npm@10.x when testing node 22, which is why it passed.
Activity
flyingduck92 commentedon Nov 3, 2025
I got same issue, when trying to make a new tanstack-router project the issue is similar -> TanStack/cli#221
patrickledew commentedon Nov 14, 2025
I'm getting the same issue when running npx with the
@asyncapi/clipackage. It does work after a couple tries, but the majority of the time I get this ECOMPROMISED error. The log shows this stack trace:To debug I added some console.logs to the with-lock.js file:
This gives the following output:
Seems like the
fs.statcall works fine the first couple calls, but then balloons to 500ms then 23 seconds! While this is hanging, the interval for touchLock keeps getting called. When the next fs.stat call eventually succeeds it seems to use an old value for the current mtime which triggers the abort.For context, this is running on a Windows 365 VM so I'm unsure if that would affect fs.stat performance.
Node version: v25.2.0
NPM version: 11.6.2
cc: @jenseng
jenseng commentedon Nov 14, 2025
Thanks for the ping and the detailed investigation @patrickledew! I'll take a stab at fixing this. The 23 second stat calls are definitely surprising 😱
jenseng commentedon Nov 14, 2025
Given that it only happens in windows + node 24, it sounds like there may be a bug in node that's causing the long stat calls. But hopefully we can protect against that (e.g. perhaps a higher staleThreshold, and change from setInterval -> setTimeout)
jenseng commentedon Nov 14, 2025
Looks like it's not specific to node 24, it happens in node 22 as well. The test.yml workflow in the PR description is actually using npm@10.x when testing node 22, which is why it passed.
I've just about got a fix ready, which seems to do the trick 🥳
Work around an npm regression
Work around an npm regression (#1768)
fix(libnpmexec): fix lock compromise logic (#8733)
Expand test matrix with 24.x runtime (#14300)
Circleci project setup (#64)
Create two-taxis-whisper.md (#1) clean bug api limit (#77)
fix(postinstall): bypass npx entirely for lark-cli skills install (fixes
fix(postinstall): use npx -y for lark-cli skills install
CI: install the PUBLISHED package, on every platform