ENOENT error when the same package appears in local dependencies

What I Wanted to Do

Running npm install and expecting packages to be installed.

Cloning the git repo below will help to understand the issue.
As you can see there, a project has 3 directories (comp-a, comp-b and comp-c), each one is a package, all have the same package dependency (classnames but can be anything).
In addition, the first package (comp-a) has the other two packages as dependencies with relative paths.
The second package (comp-b) has the last package (comp-c) as a dependency with a relative path.

What Happened Instead

Got the following error

➜  comp-a npm i
npm WARN rollback Rolling back classnames@2.2.6 failed (this is probably harmless): /private/tmp/npm-issue/components/comp-c/node_modules/classnames is not a child of /private/tmp/npm-issue/components/comp-a
npm WARN comp-a@1.0.0 No description
npm WARN comp-a@1.0.0 No repository field.

npm ERR! code ENOENT
npm ERR! syscall rename
npm ERR! path /private/tmp/npm-issue/components/comp-a/node_modules/.staging/classnames-00a850cf
npm ERR! dest /private/tmp/npm-issue/components/comp-c/node_modules/classnames
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory, rename '/private/tmp/npm-issue/components/comp-a/node_modules/.staging/classnames-00a850cf' -> '/private/tmp/npm-issue/components/comp-c/node_modules/classnames'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/davidfirst/.npm/_logs/2019-09-17T16_13_15_580Z-debug.log
➜  comp-a

Reproduction Steps

  1. git clone https://github.com/davidfirst/npm-reproducible-bug.git
  2. cd npm-reproducible-bug/components/comp-a
  3. npm install

Details

npm-debug.log file is inside the same git repo:

Investigating the log file, it seems like that NPM installs classmate package on a temp dir, then, it creates the exact same temp-dir multiple times, because in the project this package is required from multiple packages. Then, once done, it changes the temp-dir to classmate, however, since it does it multiple times, it tries to change it again from temp-dir to classmate and failed because the temp-dir doesn’t exist anymore.

Platform Info

$ npm --versions
{ 'comp-a': '1.0.0',
  npm: '6.11.3',
  ares: '1.10.1-DEV',
  cldr: '32.0',
  http_parser: '2.8.0',
  icu: '60.1',
  modules: '57',
  napi: '3',
  nghttp2: '1.32.0',
  node: '8.12.0',
  openssl: '1.0.2p',
  tz: '2017c',
  unicode: '10.0',
  uv: '1.19.2',
  v8: '6.2.414.66',
  zlib: '1.2.11' }
$ node -p process.platform
darwin

I added an automated test based on the example repo: https://github.com/npm/cli/pull/254

Thanks for the analysis and reproduction case! This is helpful.

Unfortunately, looking at the code, two things seem clear: first, you are correct, this has been broken since version 5.0.0. Second, it’s unlikely to be fixable before version 7.0.0. The changes required, while not in themselves breaking changes per se, will involve a lot of refactoring work of some components that are already being rewritten for v7.

I’ll add it to the list of cases that Arborist should handle properly, and ensure it gets in for v7.

1 Like

Thanks a lot for the quick response! Sounds like the options are:

  • Wait for npm@7 to be released (blocker for an unknown amount of time)
  • Switch back to npm@4 (comes with risks)
  • Stop using package-lock.json until npm@7 (comes with risks, not 100% sure the problem goes away)
  • Stop using local dependencies (quite involved on an existing codebase)
  • Switch to Yarn (might be involved on an existing codebase)