Skip to content
Nilay Kabariya

Entry No.069·Fixes·Updated ·3 min read

Fix: EBUSY: resource busy or locked (npm, Windows)

npm install and Node fail on Windows with EBUSY: resource busy or locked, rmdir. Reproduced: what holds the folder, how to find it, and why retrying fails.

The error, verbatim

Jump to the fix ↓
npm error code EBUSY
npm error syscall rmdir
npm error path D:\project\node_modules\esbuild
npm error errno -4082
npm error EBUSY: resource busy or locked, rmdir 'D:\project\node_modules\esbuild'
✓ Reproduced on Windows 11 Pro; Windows Server 2025 (GitHub Actions, November 2)

Tested on

OS
Windows 11 Pro; Windows Server 2025 (GitHub Actions, November 2)
Node / npm
24.14.0 / 11.9.0
Packages
esbuild 0.28.2, sharp

✓ Last re-run on GitHub Actions · see the run · all repros

Contents
  1. 1. A terminal or process sitting inside the folder (EBUSY rmdir)01
  2. 2. A program from node_modules still running (EPERM unlink)02
  3. What didn’t work03
  4. How this was tested04

Windows won’t delete a folder or file that something else is using. When npm (or a Node script) tries to, it stops with EBUSY or, just as often, EPERM. The path in the error tells you what is locked; the cause is always another process, and it’s usually one you started yourself: a dev server, a watcher, a terminal tab.

I reproduced both on Windows 11 with Node 24.14.0 and npm 11.9.0, in a project with esbuild and sharp installed, by keeping something busy and running npm ci, which deletes node_modules first.

What was holding it What npm printed
A process (terminal, dev server) whose current folder is inside node_modules EBUSY, syscall rmdir, errno -4082
esbuild.exe from node_modules still running EPERM, syscall unlink, errno -4048, path …\esbuild.exe
A Node process that has loaded sharp (a native module) EPERM, syscall unlink, path …\libvips-42.dll

So if your message says EPERM … unlink on an .exe, .dll or .node file, it’s the same problem as EBUSY, and the same fix.

1. A terminal or process sitting inside the folder (EBUSY rmdir)

With a process whose working directory was node_modules\esbuild, npm ci failed with:

npm error code EBUSY
npm error syscall rmdir
npm error path D:\nk-ebusy\proj\node_modules\esbuild
npm error errno -4082
npm error EBUSY: resource busy or locked, rmdir 'D:\nk-ebusy\proj\node_modules\esbuild'

That’s what happens when a terminal tab is cd’d into that folder, or a dev server or script was started from inside it. Windows treats a process’s current folder as in use.

Move every terminal out of the folder, and stop anything you started from there. To see which Node processes are still running and how they were started:

Get-CimInstance Win32_Process -Filter "Name='node.exe'" | Select-Object ProcessId, CommandLine

Stop the one you don’t need:

Stop-Process -Id <ProcessId>

Dev servers often run a native program straight from node_modules, like esbuild’s esbuild.exe. While it ran, npm ci couldn’t delete it:

npm error code EPERM
npm error syscall unlink
npm error path D:\nk-ebusy\proj\node_modules\@esbuild\win32-x64\esbuild.exe
npm error errno -4048

A Node process that had loaded sharp blocked it the same way, on libvips-42.dll. Native modules (.node files and their DLLs) stay locked for as long as any process has them loaded.

Stop the dev server or watcher (Ctrl+C in its terminal), then run the install again. If you can’t tell which process it is, this lists the Node processes that have a file from your project loaded:

Get-Process node | Where-Object { $_.Modules.FileName -like '*\your-project\node_modules\*' } | Select-Object Id, ProcessName

What didn’t work

How this was tested

Windows 11 Pro, Node 24.14.0, npm 11.9.0, October 15, 2026. A fresh project with esbuild 0.28.2 and sharp installed. For each case a separate process was started with PowerShell’s Start-Process (an esbuild --serve server, a Node script that loads sharp, and an idle Node process whose working directory was inside node_modules), then npm ci or Node’s fs.rmSync ran against the same folder, and finally the process was stopped by its ID and the command repeated. Every error above is the real output.

November 2, 2026: all three causes were re-run on GitHub Actions windows-latest (Windows Server 2025, Node 24.22.1, npm 11.19.1, esbuild 0.28.2, sharp 0.35.5). A process started inside node_modules\esbuild gave EBUSY / rmdir / -4082; a running esbuild.exe gave EPERM / unlink / -4048 on esbuild.exe, and fs.rmSync with maxRetries: 3 failed too; a Node process with sharp loaded gave EPERM on libvips-42.dll. Both PowerShell commands found the right process, and npm ci finished each time once it was stopped. The script and output are public.

— N.K., end of entry No.069

Useful? Pass it on:Post on XFollow @EmotionalMatter

Related entries

  1. No.093

    Fix: bigint Failed to load bindings, pure JS will be used

    bigint: Failed to load bindings, pure JS will be used. Why npm 12 shows it on every install, whether it matters, and how to get the native build back.

    > bigint: Failed to load bindings, pure JS will be used (try npm run rebuild?)

    FIXED3 minWindows
  2. No.072

    npm error code ENOENT, and spawn npx ENOENT on Windows

    Read the path line first. On Windows, spawn npx ENOENT has a twist: adding .cmd now fails with EINVAL. Four ENOENT causes reproduced, each with its fix.

    > npm error code ENOENT

    FIXED3 minWindows
  3. No.071

    Fix: JavaScript heap out of memory (Node.js, tested)

    FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory. Default limits measured, and two ways the usual fix silently fails.

    > FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

    FIXED2 minWindows

Post card · Every Sunday

What broke this week.

One email on Sundays: the Node, TypeScript, Python and AI SDK releases that broke builds that week, each one reproduced, with a link to the fix that worked. Never shared, and you can leave any time.

— Nilay

or follow by RSSor on X

By subscribing you agree to the privacy note. One click to leave.

tip: paste the exact error text