vscode icon indicating copy to clipboard operation
vscode copied to clipboard

vitest plugin keeps a file lock on rollup and esbuild which halts package upgrades at times

Open SimmeNilsson opened this issue 1 year ago • 29 comments

Describe the bug

When I upgrade my npm packages using yarn 1.x I every now and then get error Error: EPERM: operation not permitted, unlink '...\node_modules@esbuild\win32-x64\esbuild.exe' or error Error: EPERM: operation not permitted, unlink '...\node_modules@rollup\rollup-win32-x64-msvc\rollup.win32-x64-msvc.node'

The locking process belongs to the vitest VS Code plugin. I am not running any tests at the time.

Reproduction

Usually happens when I upgrade npm packages. Specifically when yarn tries to unlink either esbuild or rollup. For reproduction example and instructions see this repo: https://github.com/SimmeNilsson/FileLockVitestPlugin

Output

[INFO 4:09:05 PM] [v0.12.3] Vitest extension is activated because Vitest is installed or there is a Vite/Vitest config file in the workspace.
[INFO 4:09:09 PM] [API] Running Vitest v1.6.0 (FileLockVitestPlugin/vitest.config.mts) with Node.js: C:\Program Files\nodejs\node.EXE
[INFO 4:09:20 PM] [API] Vitest v1.6.0 (FileLockVitestPlugin/vitest.config.mts) process 59084 created
[INFO 4:09:23 PM] [API] Collecting tests: test/App.spec.ts

Version

0.12.3

Validations

SimmeNilsson avatar Jun 18 '24 09:06 SimmeNilsson

I cannot reproduce this behavior. If package.json is updated, Vitest will close the running process and start a new one - maybe something happens during this?

None from vitest itself.

The output channel will contain at least the version, so if you don't see anything there, then you don't have the extension installed. Did you actually check it?

sheremet-va avatar Jun 18 '24 11:06 sheremet-va

Hello @SimmeNilsson. Please provide a minimal reproduction using a GitHub repository or StackBlitz (you can also use examples). Issues marked with needs reproduction will be closed if they have no activity within 3 days.

github-actions[bot] avatar Jun 18 '24 11:06 github-actions[bot]

I've updated my initial description with output and reproduction repo.

Steps described in the README of the repo. I think this approach should be pretty reliable. Basically one needs to trigger an upgrade/downgrade of either rollup or esbuild (after making sure the plugin has been exercised) to see the issue.

As for the output, it is not when I exercise the test that I have the problem. When running it is just normal output. It is when I perform my package upgrade and yarn tries to unlink rollup and/or esbuild that I get the problem. That was why I wrote like I initially did.

Edit: The file locking process in my last repro was "C:\Program Files\nodejs\node.EXE" --dns-result-order=ipv4first C:/Users/.../.vscode/extensions/vitest.explorer-0.12.3/dist/worker.js

SimmeNilsson avatar Jun 18 '24 14:06 SimmeNilsson

Output

Is this the full output at the moment of getting an error? Did you remove anything from it?

sheremet-va avatar Jun 18 '24 14:06 sheremet-va

It is the full output. image As the tests run fine, should we expect anything more? The problem I have is the file locks it seems to be keeping around rollup and esbuild when it isn't actively running tests.

The only error output I have is the one I get from yarn regarding the EPERM. And when I track down the process responsible for holding the file (through Process Explorer), that process belongs to the plugin as far as I can tell (CLI line in last post).

SimmeNilsson avatar Jun 18 '24 14:06 SimmeNilsson

[INFO 4:34:20 PM] [v0.12.3] Vitest extension is activated because Vitest is installed or there is a Vite/Vitest config file in the workspace. [INFO 4:34:22 PM] [API] Running Vitest v1.6.0 (FileLockVitestPlugin/vitest.config.mts) with Node.js: C:\Program Files\nodejs\node.EXE [INFO 4:34:23 PM] [API] Vitest v1.6.0 (FileLockVitestPlugin/vitest.config.mts) process 66220 created [INFO 4:34:25 PM] [API] Collecting tests: test/App.spec.ts [INFO 4:36:01 PM] Running 1 file(s) with name pattern: ^\s?App math adds up$ [Worker] Collecting tests due to file changes: package.jso [INFO 4:36:28 PM] [API] Vitest process 66220 closed successfully [INFO 4:36:28 PM] [API] Running Vitest v1.6.0 (FileLockVitestPlugin/vitest.config.mts) with Node.js: C:\Program Files\nodejs\node.EXE [INFO 4:36:29 PM] [API] Vitest v1.6.0 (FileLockVitestPlugin/vitest.config.mts) process 76060 created

I think I understand what you meant now. That it restarts when package.json changes. However, within one second a new process is created. If the unlinking of esbuild/rollup doesn't happen within that window, then a lock will be present (at least it seems to be for me).

In the two scenarios I've provided that window is either missed (A) or not present at all (B).

Scenario A I manually add a resolution (can't be done through the yarn 1.x CLI AFAIK) to package.json. Plugin process is automatically restarted. Then I run yarn (install) to have package.json change reflected in node_modules and yarn.lock.

Scenario B I delete the lock file and run yarn to recreate it, which might result in an upgrade of installed (within ranges given in package.json) esbuild or rollup. package.json never touched.

Edit: Scenario C Branch switch then yarn (install) to sync node_modules.

SimmeNilsson avatar Jun 18 '24 14:06 SimmeNilsson

Any luck in reproducing given the scenarios detailed above?

SimmeNilsson avatar Aug 05 '24 12:08 SimmeNilsson

I get this when switching between branches that have different versions of esbuild or rollup in yarn.lock. Deactivating the extension and clicking "restart extensions" allows yarn install to work. Reactivating the extension (even without running any tests) causes the EPERM error again when doing yarn install again

MetRonnie avatar Aug 15 '24 17:08 MetRonnie

I get this when switching between branches that have different versions of esbuild or rollup in yarn.lock. Deactivating the extension and clicking "restart extensions" allows yarn install to work. Reactivating the extension (even without running any tests) causes the EPERM error again when doing yarn install again

Do you use Windows?

sheremet-va avatar Aug 20 '24 12:08 sheremet-va

Yes

MetRonnie avatar Aug 20 '24 13:08 MetRonnie

Oh I keep getting that one and I was wondering where it was coming from. So annoying, I have to manually delete the node modules before rebuilding my code...

ocombe avatar Aug 29 '24 15:08 ocombe

Does this happen only with yarn 1? Are there any other instances of this happening with other package managers?

sheremet-va avatar Sep 08 '24 12:09 sheremet-va

Yarn 4.4.1 here

MetRonnie avatar Sep 09 '24 09:09 MetRonnie

So, the issue here is that the Vitest extension constantly maintains Vitest running in the background for an instant response. This typically functions well as package managers generally do not delete everything from the node_modules, but it seems yarn operates differently. I have a few suggestions to offer:

  1. Add a command to stop the Vitest extension. You can run it before updating dependencies.
  2. Add an option to use an old-fashioned Vitest extension that would spawn a new Vitest instance every time you click on a test. The tests will take longer to run in this case (debug already works like this, for example).

sheremet-va avatar Sep 09 '24 12:09 sheremet-va

I get this error often when running npm install from a task in VSCode. This was my situation, I don't know which steps are important, this is just what I did this time.

  1. Setup, latest VSCode on Windows, with plenty of extensions like vitest, eslint, jest, GitLens, and more.
  2. First, From VSCode UI, I changed the branch from some old branch, to a branch where vitest was being upgraded from v2 to v3. (both the package.json and package-lock.json have been changed).
  3. Then, Then from the Terminal->Run task..., I select the task that updates all our projects. (there are 6 of them, which is why I made a task to do it.)
  4. Finally, I get this error in both projects that use vitest:
npm warn cleanup Failed to remove some directories [
    + CategoryInfo          : NotSpecified: (npm warn cleanu...e directories [:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
    + PSComputerName        : localhost
 
npm warn cleanup   [
npm warn cleanup     'C:\\project-path\\node_modules\\.vitest-v5BY0obU',
npm warn cleanup     [Error: EPERM: operation not permitted, unlink 
'C:\project-path\node_modules\.vitest-v5BY0obU\node_modules\@esbuild\win32-x64\esbuild.exe'] {
npm warn cleanup       errno: -4048,
npm warn cleanup       code: 'EPERM',
npm warn cleanup       syscall: 'unlink',
npm warn cleanup       path: 'C:\\project-path\\node_modules\\.vitest-v5BY0obU\\node_modules\\@esbuild\\win32-x64\\esbuild.exe'
npm warn cleanup     }
npm warn cleanup   ]
npm warn cleanup ]

jerekm avatar Feb 12 '25 17:02 jerekm

Also getting this after switching branches and then trying to npm ci to install the packages for that branch. The extension is keeping the esbuild process open forever.

Image

Would it be possible for you to make this extension copy the esbuild executable to another location before launching it?

ascott18 avatar Feb 18 '25 17:02 ascott18

I have a few suggestions to offer:

  1. Add a command to stop the Vitest extension. You can run it before updating dependencies.
  2. Add an option to use an old-fashioned Vitest extension that would spawn a new Vitest instance every time you click on a test. The tests will take longer to run in this case (debug already works like this, for example).

Maybe a third option could be that the vitest runner times out after a period of inactivity, say 2 minutes after the last test was run.

MetRonnie avatar Feb 18 '25 17:02 MetRonnie

Maybe a third option could be that the vitest runner times out after a period of inactivity, say 2 minutes after the last test was run.

Looks like esbuild has a stop method. Maybe we can call it if the extension is in idle mode (how to decide what idle mode is?)

sheremet-va avatar Feb 18 '25 18:02 sheremet-va

Maybe a third option could be that the vitest runner times out after a period of inactivity, say 2 minutes after the last test was run.

eh..., you'd still get the error occasionally, since eventually you will run npm ci within those 2 minutes.

jerekm avatar Feb 18 '25 18:02 jerekm

So, the issue here is that the Vitest extension constantly maintains Vitest running in the background for an instant response. This typically functions well as package managers generally do not delete everything from the node_modules, but it seems yarn operates differently. I have a few suggestions to offer:

  1. Add a command to stop the Vitest extension. You can run it before updating dependencies.
  2. Add an option to use an old-fashioned Vitest extension that would spawn a new Vitest instance every time you click on a test. The tests will take longer to run in this case (debug already works like this, for example).

I would go for option 2. But I usually just use the extension to debug.

SimmeNilsson avatar Mar 18 '25 14:03 SimmeNilsson

Ah, so it was the Vitest extension that was causing me those issues. I've just been closing VSCode when I run into that issue, then restarting it after I've run npm [c]i.

I'm not sure there's any way to truly fix the issue without the extension caching a copy of the executables it needs outside of node_modules.

  • Adding a command to stop/restart the extension isn't much different than the user just disabling the extension before running their package update command and then re-enabling it after; it's basically just making the workaround official (but it's still a workaround).
  • Adding an option to make the performance worse all the time just for the occasional times the user needs to update certain dependencies seems rather extreme / a trap option.

DayKev avatar May 21 '25 06:05 DayKev

Same here, possible workaround to force-stop node.exe on each ci

HeadOnDanielG avatar May 23 '25 09:05 HeadOnDanielG

@HeadOnDanielG I had a go at this and came up with this task configuration for VS Code:

{
    "tasks": [
        {
            "type": "shell",
            "problemMatcher": [],
            "label": "kill vitest extension",
            "command": "Get-CimInstance",
            "args": [
                "-ClassName",
                "Win32_Process",
                "-Filter",
                "'Name = ''node.exe'' and CommandLine like ''%vitest.explorer%'''",
                "|",
                "Stop-Process",
                "-Id",
                "{$_.ProcessId}",
            ]
        },
        {
            "type": "npm",
            "script": "install",
            "dependsOn": [
                "kill vitest extension"
            ],
            "problemMatcher": [],
            "label": "npm: install",
            "detail": "install dependencies from package"
        }
    ]
}

MetRonnie avatar May 27 '25 12:05 MetRonnie

@HeadOnDanielG I had a go at this and came up with this task configuration for VS Code:

{ "tasks": [ { "type": "shell", "problemMatcher": [], "label": "kill vitest extension", "command": "Get-CimInstance", "args": [ "-ClassName", "Win32_Process", "-Filter", "'Name = ''node.exe'' and CommandLine like ''%vitest.explorer%'''", "|", "Stop-Process", "-Id", "{$_.ProcessId}", ] }, { "type": "npm", "script": "install", "dependsOn": [ "kill vitest extension" ], "problemMatcher": [], "label": "npm: install", "detail": "install dependencies from package" } ] }

thanks for that. ill give it a try.

HeadOnDanielG avatar Jun 06 '25 11:06 HeadOnDanielG

Why was this labeled as an edge-case? It happens to everyone who uses the extension and runs npm ci.

orgads avatar Jun 16 '25 09:06 orgads

Why was this labeled as an edge-case? It happens to everyone who uses the extension and runs npm ci.

Based on the label description, I think it's because there's a workaround (close VSCode or temporarily disable the extension). Image

DayKev avatar Jun 16 '25 12:06 DayKev

Windows 11 24h2 Yarn Classic, then PNPM 10.14 Getting this problem sometimes. Today I opened VScode, open built-in terminal, run update of dependency. Got error. Then stopped add-on. Update went ok.

Image

inoyakaigor avatar Aug 22 '25 09:08 inoyakaigor

I made a yarn plugin out of the powershell command @MetRonnie shared

it runs during the validation project hook stopping Vitest node process.

The code is here yarn-plugin-vscode-vitest-extension-terminate

you can install it as a plugin through jsdelivr.com cdn like this:

yarn plugin import https://cdn.jsdelivr.net/gh/pierluigigiancola/yarn-plugin-vscode-vitest-extension-terminate@main/index.js

Thanks again @MetRonnie for the script 🙏

pierluigigiancola avatar Oct 28 '25 10:10 pierluigigiancola