npm install with relative path dependency fails when transitive dependency has a different relative path

What I Wanted to Do

Run npm install on a package using relative file paths where the root package has the following dependencies:

  • root -> a
  • root -> b -> a

and the following folder structure:

ā”œā”€ā”€ helpers
ā”‚   ā””ā”€ā”€ a
ā”‚       ā”œā”€ā”€ package-lock.json
ā”‚       ā””ā”€ā”€ package.json
ā”œā”€ā”€ solution
ā”‚   ā”œā”€ā”€ root
ā”‚   ā”‚   ā”œā”€ā”€ package-lock.json
ā”‚   ā”‚   ā””ā”€ā”€ package.json
ā”‚   ā””ā”€ā”€ lib
ā”‚       ā””ā”€ā”€ b
ā”‚           ā”œā”€ā”€ package-lock.json
ā”‚           ā””ā”€ā”€ package.json

6 directories, 7 files

I expected that running npm install in solution/root would work after running npm install in solution/lib/b

What Happened Instead

Running npm install in solution/root fails with the following error message:

Installing /sandbox/npm-relative-module-resolution/solution/root
npm ERR! code ENOLOCAL
npm ERR! Could not install from "../helpers/a" as it does not contain a package.json file.

npm ERR! A complete log of this run can be found in:
npm ERR!     /sandbox/.npm/_logs/2019-06-14T20_24_50_780Z-debug.log

Please notice how in the error message, the first relative path component ā€œ../ā€ is not present

Note: sanitized file paths

Reproduction Steps

The easiest way to reproduce is to clone and run the commands found in in the root directory.

Here is the sequence to reproduce manually:


  1. $ mkdir reproduction && cd reproduction

  2. $ mkdir -p helpers/a solution/root solution/lib/b

  3. $ (cd helpers/a && npm init -y)

  4. $ (cd solution/lib/b && npm init -y && npm install --save file:../../../helpers/a)

  5. $ (cd solution/root && npm init -y && npm install --save file:../../helpers/a file:../lib/b)

  6. $ find . -type d -name node_modules -exec rm -rf '{}' ';' # reset to clean state

  7. $ (cd solution/lib/b && npm install) # install dependency

Now run:

(cd solution/root && npm install)


  • In the error message note how one of the .. is elided.
  • Restructuring the directories so that the transitive dependency has a different looking tree (except for the .. components) fixes the problem.

An example npm-debug.log file:

0 info it worked if it ends with ok
1 verbose cli [ '/sandbox/.nvm/versions/node/v10.16.0/bin/node',
1 verbose cli   '/sandbox/.nvm/versions/node/v10.16.0/bin/npm',
1 verbose cli   'install' ]
2 info using npm@6.9.0
3 info using node@v10.16.0
4 verbose npm-session e4ad6050a2ae9ff7
5 silly install runPreinstallTopLevelLifecycles
6 silly preinstall root@1.0.0
7 info lifecycle root@1.0.0~preinstall: root@1.0.0
8 silly install loadCurrentTree
9 silly install readLocalPackageData
10 timing stage:loadCurrentTree Completed in 7ms
11 silly install loadIdealTree
12 silly install cloneCurrentTreeToIdealTree
13 timing stage:loadIdealTree:cloneCurrentTree Completed in 0ms
14 silly install loadShrinkwrap
15 timing stage:loadIdealTree:loadShrinkwrap Completed in 6ms
16 silly install loadAllDepsIntoIdealTree
17 silly fetchPackageMetaData error for a@file:../../helpers/a Could not install from "../helpers/a" as it does not contain a package.json file.
18 timing stage:rollbackFailedOptional Completed in 0ms
19 timing stage:runTopLevelLifecycles Completed in 45ms
20 silly saveTree root@1.0.0
20 silly saveTree ā”œā”€ā”€ a@file:../../helpers/a
20 silly saveTree ā””ā”€ā”€ b@file:../lib/b
21 verbose stack Error: ENOENT: no such file or directory, open '/private/tmp/tst/solution/helpers/a/package.json'
22 verbose cwd /private/tmp/tst/solution/root
23 verbose Darwin 17.7.0
24 verbose argv "/sandbox/.nvm/versions/node/v10.16.0/bin/node" "/sandbox/.nvm/versions/node/v10.16.0/bin/npm" "install"
25 verbose node v10.16.0
26 verbose npm  v6.9.0
27 error code ENOLOCAL
28 error Could not install from "../helpers/a" as it does not contain a package.json file.
29 verbose exit [ 1, true ]

Note: file path sanitized

Platform Info

$ npm --versions
{ npm: '6.9.0',
  ares: '1.15.0',
  brotli: '1.0.7',
  cldr: '35.1',
  http_parser: '2.8.0',
  icu: '64.2',
  modules: '64',
  napi: '4',
  nghttp2: '1.34.0',
  node: '10.16.0',
  openssl: '1.1.1b',
  tz: '2019a',
  unicode: '12.1',
  uv: '1.28.0',
  v8: '',
  zlib: '1.2.11' }

$ node -p process.platform

@jncornett Having the same issues. Iā€™m using the following workaround:

  • Delete package-lock.json
  • Run npm install