cli icon indicating copy to clipboard operation
cli copied to clipboard

[BUG] Installing package with optional peer dependencies reports conflicts incorrectly

Open gkjohnson opened this issue 5 months ago • 9 comments

Is there an existing issue for this?

  • [x] I have searched the existing issues

This issue exists in the latest npm version

  • [x] I am using the latest npm

Current Behavior

I'm using optional peer dependencies for maintaining the 3DTilesRenderer package to ensure only the dependencies that users need are installed. As far as I can tell there are no conflicts within the peer dependency definitions but when installing from npm in a new project via npm install 3d-tiles-renderer I get the following error:

npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: [email protected]
npm error Found: [email protected]
npm error node_modules/react
npm error   peer react@"*" from [email protected]
npm error   node_modules/expo
npm error     peerOptional expo@">=43.0" from @react-three/[email protected]
npm error     node_modules/@react-three/fiber
npm error       peerOptional @react-three/fiber@"^8.17.9" from [email protected]
npm error       node_modules/3d-tiles-renderer
npm error         3d-tiles-renderer@"*" from the root project
npm error     peer expo@"*" from @expo/[email protected]
npm error     node_modules/@expo/dom-webview
npm error       peerOptional @expo/dom-webview@"*" from [email protected]
npm error     3 more (expo-asset, expo-file-system, expo-gl)
npm error   peer react@"*" from @expo/[email protected]
npm error   node_modules/@expo/dom-webview
npm error     peerOptional @expo/dom-webview@"*" from [email protected]
npm error   5 more (react-native, react-native-webview, expo-asset, expo-gl, react-native-web)
npm error
npm error Could not resolve dependency:
npm error peerOptional react@"^18.3.1" from [email protected]
npm error node_modules/3d-tiles-renderer
npm error   3d-tiles-renderer@"*" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.

When specifying the same packages as "dependencies" in the same package.json, though, and installing them there are no conflicts. So it seems the optional peer dependencies are being reported incorrectly.

Expected Behavior

No errors should be reported when installing a package with optional peer dependencies when there are no conflicts. It's not clear to me why conflicts are being checked at all when the project has not separately installed the peer dependencies.

Steps To Reproduce

  1. Create a new folder
  2. Run npm init
  3. Run npm install 3d-tiles-renderer
  4. See error reporting conflicts even though no other packages are installed and the package has no required dependencies other than 1 peer dependency (three.js) which has no dependencies itself.

Environment

  • npm: 11.4.2
  • Node.js: 24.3.0
  • OS Name: Sequoia 15.5
  • System Model Name: 2021 M1 Macbook Pro
  • npm config:
; "user" config from /Users/garrett/.npmrc

//registry.npmjs.org/:_authToken = (protected)

; node bin location = /Users/garrett/.nvm/versions/node/v24.3.0/bin/node
; node version = v24.3.0
; npm local prefix = /Users/garrett/Desktop/test
; npm version = 11.4.2
; cwd = /Users/garrett/Desktop/test
; HOME = /Users/garrett
; Run `npm config ls -l` to show all defaults.

gkjohnson avatar Jul 04 '25 03:07 gkjohnson

u can use force npm install --force 3d-tiles-renderer it will work

amr807 avatar Jul 04 '25 03:07 amr807

I have a guess about this kind of error.

In my observation, npm does not (eventually) install a peer dep with optional: true in peerDependenciesMeta. But it does check possible version conflict, between installed packages and that could be introduced by installing the optional peer dep. In your case the problem is another possible react introduced by optional expo, by optional @react-three/fiber.

In my similar case I worked it around by adding a overrides: {react:} in workspace root package.json . npm i succeeded for me and the resulted package-lock did not have the optional deps of any depth.

This is not documented well in doc but I'm guessing it's intended. It feels too complicated to be a accidental bug.

jokester avatar Jul 05 '25 02:07 jokester

Thanks @jokester - I thought this might be the case but if it were true that the peer dependencies were conflicting among themselves (these are the only dependencies in 3d-tiles-renderer) then I would expect a conflict error to log when installing all packages as normal dependencies, but that doesn't seem to happen.

When configuring a new projects package.json with the same dependencies like so (these are the same ones as specified in 3d-tiles-renderer package.json) they all install as expected (with or without version range modifiers likes >= or ^):

{
  "dependencies": {
    "@react-three/fiber": "^8.17.9",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "three": ">=0.166.0"
  }
}

I would try seeing if changing the versions used fixes this but I can't figure out how to reproduce this without publishing the package to npm. Specifying the packages in peer dependencies in a local project doesn't repro the conflict error. Neither does specifying them as dependencies. And doing something like "installing" a package from a local folder like so doesn't repro it because it just seems to link the folder:

"3d-tiles-renderer": "file:./path/to/3d-tiles-renderer",

gkjohnson avatar Jul 05 '25 03:07 gkjohnson

@gkjohnson In you example changing react version to ^19 will cause ERESOLVE unable to resolve dependency tree.

The following example shows my theory better, because the conflicting versions are react@^19, a must dep, and react^18.3.1 , a optional peer dep of 3d-tiles-renderer:

{
  "name": "repro",
  "dependencies": {
    "3d-tiles-renderer": "^0.4.11",
    "react": "^19"
  }
}

It seems to me the default resolution algorithm only wants to allow 2 cases:

  1. a optional peer dep (this may include transitive deps) not installed (any version)
  2. or, a optional peer dep installed (react) and the version not violating the peer declared it (3d-tiles-renderer)

jokester avatar Jul 05 '25 04:07 jokester

@jokester I may not be understanding -

{
  "name": "repro",
  "dependencies": {
    "3d-tiles-renderer": "^0.4.11",
    "react": "^19"
  }
}

☝ This case I understand because react is installed at version 19 but the peer dependencies of 3d-tiles-renderer are saying "if react is installed it must meet these version requirements" (ie must use react v18).

But that's not what's happening in my example. My repro steps are starting from a completely fresh project and package.json with no other dependencies. That's a completely empty package.json, no package-lock.json, and no node_modules folder:

// package.json
{
  "name": "test"
}

Then from that directory run npm install 3d-tiles-renderer and you'll see that there's an ERESOLVE unable to resolve dependency tree error logged. But there are no dependencies other than those in 3d-tiles-renderer that could conflict because there are no other dependencies installed and the dependencies within 3d-tiles-renderer have been shown to not be conflicting.

gkjohnson avatar Jul 05 '25 06:07 gkjohnson

So the deeper cause could be like, an usable react version does exist but npm won't use it automatically? Since specifying that version works, maybe npm failed to find it by itself.

jokester avatar Jul 05 '25 06:07 jokester

Looking at this again I'm wondering if there is some issue with the logic in comparing the compatibility of semver ranges when there is no concrete package version available. Looking at the error again it says it "found react 19" which is incompatible with "react 18" specified as a peer dependency:

Found: [email protected]
...
Could not resolve dependency:
peerOptional react@"^18.3.1" from [email protected]

But looking at the dependency tree react 19 isn't specified anywhere - expo is just specifying react@* which should match every version of react (and therefore the peer dependency specifier) but instead it's saying it found react 19:

Found: [email protected]
  peer react@"*" from [email protected]

The same thing happens with other dependencies like react-native. The "optional peer dependency" for react-native is set to react-native@">=0.64" in react-three/fiber but instead npm infers the latest version of react-native which relies on react 19 even though a compatible, lower version of react-native would likely depend on a version of react that is compatible with the 3d tiles renderer peer dependency:

  peer react@"^19.1.0" from [email protected]
  node_modules/react-native
    peerOptional react-native@">=0.64" from @react-three/[email protected]

Here's the full error text from the log written to disk:

Full error log
# npm resolution error report

While resolving: [email protected]
Found: [email protected]
node_modules/react
  peer react@"*" from [email protected]
  node_modules/expo
    peerOptional expo@">=43.0" from @react-three/[email protected]
    node_modules/@react-three/fiber
      peerOptional @react-three/fiber@"^8.17.9" from [email protected]
      node_modules/3d-tiles-renderer
        3d-tiles-renderer@"*" from the root project
    peer expo@"*" from @expo/[email protected]
    node_modules/@expo/dom-webview
      peerOptional @expo/dom-webview@"*" from [email protected]
    peer expo@"*" from [email protected]
    node_modules/expo-asset
      peerOptional expo-asset@">=8.4" from @react-three/[email protected]
      node_modules/@react-three/fiber
        peerOptional @react-three/fiber@"^8.17.9" from [email protected]
        node_modules/3d-tiles-renderer
          3d-tiles-renderer@"*" from the root project
      expo-asset@"~11.1.7" from [email protected]
    peer expo@"*" from [email protected]
    node_modules/expo-file-system
      peerOptional expo-file-system@">=11.0" from @react-three/[email protected]
      node_modules/@react-three/fiber
        peerOptional @react-three/fiber@"^8.17.9" from [email protected]
        node_modules/3d-tiles-renderer
          3d-tiles-renderer@"*" from the root project
      expo-file-system@"~18.1.11" from [email protected]
    peer expo@"*" from [email protected]
    node_modules/expo-gl
      peerOptional expo-gl@">=11.0" from @react-three/[email protected]
      node_modules/@react-three/fiber
        peerOptional @react-three/fiber@"^8.17.9" from [email protected]
        node_modules/3d-tiles-renderer
          3d-tiles-renderer@"*" from the root project
  peer react@"*" from @expo/[email protected]
  node_modules/@expo/dom-webview
    peerOptional @expo/dom-webview@"*" from [email protected]
  peer react@"^19.1.0" from [email protected]
  node_modules/react-native
    peerOptional react-native@">=0.64" from @react-three/[email protected]
    node_modules/@react-three/fiber
      peerOptional @react-three/fiber@"^8.17.9" from [email protected]
      node_modules/3d-tiles-renderer
        3d-tiles-renderer@"*" from the root project
    peer react-native@"*" from [email protected]
    node_modules/expo
      peerOptional expo@">=43.0" from @react-three/[email protected]
      node_modules/@react-three/fiber
        peerOptional @react-three/fiber@"^8.17.9" from [email protected]
        node_modules/3d-tiles-renderer
          3d-tiles-renderer@"*" from the root project
      peer expo@"*" from @expo/[email protected]
      node_modules/@expo/dom-webview
        peerOptional @expo/dom-webview@"*" from [email protected]
      peer expo@"*" from [email protected]
      node_modules/expo-asset
        peerOptional expo-asset@">=8.4" from @react-three/[email protected]
        node_modules/@react-three/fiber
          peerOptional @react-three/fiber@"^8.17.9" from [email protected]
          node_modules/3d-tiles-renderer
            3d-tiles-renderer@"*" from the root project
        expo-asset@"~11.1.7" from [email protected]
      peer expo@"*" from [email protected]
      node_modules/expo-file-system
        peerOptional expo-file-system@">=11.0" from @react-three/[email protected]
        node_modules/@react-three/fiber
          peerOptional @react-three/fiber@"^8.17.9" from [email protected]
          node_modules/3d-tiles-renderer
            3d-tiles-renderer@"*" from the root project
        expo-file-system@"~18.1.11" from [email protected]
      peer expo@"*" from [email protected]
      node_modules/expo-gl
        peerOptional expo-gl@">=11.0" from @react-three/[email protected]
        node_modules/@react-three/fiber
          peerOptional @react-three/fiber@"^8.17.9" from [email protected]
          node_modules/3d-tiles-renderer
            3d-tiles-renderer@"*" from the root project
    peer react-native@"*" from @expo/[email protected]
    node_modules/@expo/dom-webview
      peerOptional @expo/dom-webview@"*" from [email protected]
    peer react-native@"*" from @expo/[email protected]
    node_modules/@expo/metro-runtime
      peerOptional @expo/metro-runtime@"*" from [email protected]
      node_modules/expo
        peerOptional expo@">=43.0" from @react-three/[email protected]
        node_modules/@react-three/fiber
          peerOptional @react-three/fiber@"^8.17.9" from [email protected]
          node_modules/3d-tiles-renderer
            3d-tiles-renderer@"*" from the root project
        peer expo@"*" from @expo/[email protected]
        node_modules/@expo/dom-webview
          peerOptional @expo/dom-webview@"*" from [email protected]
        peer expo@"*" from [email protected]
        node_modules/expo-asset
          peerOptional expo-asset@">=8.4" from @react-three/[email protected]
          node_modules/@react-three/fiber
            peerOptional @react-three/fiber@"^8.17.9" from [email protected]
            node_modules/3d-tiles-renderer
              3d-tiles-renderer@"*" from the root project
          expo-asset@"~11.1.7" from [email protected]
        peer expo@"*" from [email protected]
        node_modules/expo-file-system
          peerOptional expo-file-system@">=11.0" from @react-three/[email protected]
          node_modules/@react-three/fiber
            peerOptional @react-three/fiber@"^8.17.9" from [email protected]
            node_modules/3d-tiles-renderer
              3d-tiles-renderer@"*" from the root project
          expo-file-system@"~18.1.11" from [email protected]
        peer expo@"*" from [email protected]
        node_modules/expo-gl
          peerOptional expo-gl@">=11.0" from @react-three/[email protected]
          node_modules/@react-three/fiber
            peerOptional @react-three/fiber@"^8.17.9" from [email protected]
            node_modules/3d-tiles-renderer
              3d-tiles-renderer@"*" from the root project
    peer react-native@"*" from [email protected]
    node_modules/react-native-webview
      peerOptional react-native-webview@"*" from [email protected]
      node_modules/expo
        peerOptional expo@">=43.0" from @react-three/[email protected]
        node_modules/@react-three/fiber
          peerOptional @react-three/fiber@"^8.17.9" from [email protected]
          node_modules/3d-tiles-renderer
            3d-tiles-renderer@"*" from the root project
        peer expo@"*" from @expo/[email protected]
        node_modules/@expo/dom-webview
          peerOptional @expo/dom-webview@"*" from [email protected]
        peer expo@"*" from [email protected]
        node_modules/expo-asset
          peerOptional expo-asset@">=8.4" from @react-three/[email protected]
          node_modules/@react-three/fiber
            peerOptional @react-three/fiber@"^8.17.9" from [email protected]
            node_modules/3d-tiles-renderer
              3d-tiles-renderer@"*" from the root project
          expo-asset@"~11.1.7" from [email protected]
        peer expo@"*" from [email protected]
        node_modules/expo-file-system
          peerOptional expo-file-system@">=11.0" from @react-three/[email protected]
          node_modules/@react-three/fiber
            peerOptional @react-three/fiber@"^8.17.9" from [email protected]
            node_modules/3d-tiles-renderer
              3d-tiles-renderer@"*" from the root project
          expo-file-system@"~18.1.11" from [email protected]
        peer expo@"*" from [email protected]
        node_modules/expo-gl
          peerOptional expo-gl@">=11.0" from @react-three/[email protected]
          node_modules/@react-three/fiber
            peerOptional @react-three/fiber@"^8.17.9" from [email protected]
            node_modules/3d-tiles-renderer
              3d-tiles-renderer@"*" from the root project
    peer react-native@"*" from [email protected]
    node_modules/expo-asset
      peerOptional expo-asset@">=8.4" from @react-three/[email protected]
      node_modules/@react-three/fiber
        peerOptional @react-three/fiber@"^8.17.9" from [email protected]
        node_modules/3d-tiles-renderer
          3d-tiles-renderer@"*" from the root project
      expo-asset@"~11.1.7" from [email protected]
    peer react-native@"*" from [email protected]
    node_modules/expo-file-system
      peerOptional expo-file-system@">=11.0" from @react-three/[email protected]
      node_modules/@react-three/fiber
        peerOptional @react-three/fiber@"^8.17.9" from [email protected]
        node_modules/3d-tiles-renderer
          3d-tiles-renderer@"*" from the root project
      expo-file-system@"~18.1.11" from [email protected]
    peer react-native@"*" from [email protected]
    node_modules/expo-gl
      peerOptional expo-gl@">=11.0" from @react-three/[email protected]
      node_modules/@react-three/fiber
        peerOptional @react-three/fiber@"^8.17.9" from [email protected]
        node_modules/3d-tiles-renderer
          3d-tiles-renderer@"*" from the root project
  peer react@"*" from [email protected]
  node_modules/react-native-webview
    peerOptional react-native-webview@"*" from [email protected]
    node_modules/expo
      peerOptional expo@">=43.0" from @react-three/[email protected]
      node_modules/@react-three/fiber
        peerOptional @react-three/fiber@"^8.17.9" from [email protected]
        node_modules/3d-tiles-renderer
          3d-tiles-renderer@"*" from the root project
      peer expo@"*" from @expo/[email protected]
      node_modules/@expo/dom-webview
        peerOptional @expo/dom-webview@"*" from [email protected]
      peer expo@"*" from [email protected]
      node_modules/expo-asset
        peerOptional expo-asset@">=8.4" from @react-three/[email protected]
        node_modules/@react-three/fiber
          peerOptional @react-three/fiber@"^8.17.9" from [email protected]
          node_modules/3d-tiles-renderer
            3d-tiles-renderer@"*" from the root project
        expo-asset@"~11.1.7" from [email protected]
      peer expo@"*" from [email protected]
      node_modules/expo-file-system
        peerOptional expo-file-system@">=11.0" from @react-three/[email protected]
        node_modules/@react-three/fiber
          peerOptional @react-three/fiber@"^8.17.9" from [email protected]
          node_modules/3d-tiles-renderer
            3d-tiles-renderer@"*" from the root project
        expo-file-system@"~18.1.11" from [email protected]
      peer expo@"*" from [email protected]
      node_modules/expo-gl
        peerOptional expo-gl@">=11.0" from @react-three/[email protected]
        node_modules/@react-three/fiber
          peerOptional @react-three/fiber@"^8.17.9" from [email protected]
          node_modules/3d-tiles-renderer
            3d-tiles-renderer@"*" from the root project
  peer react@"*" from [email protected]
  node_modules/expo-asset
    peerOptional expo-asset@">=8.4" from @react-three/[email protected]
    node_modules/@react-three/fiber
      peerOptional @react-three/fiber@"^8.17.9" from [email protected]
      node_modules/3d-tiles-renderer
        3d-tiles-renderer@"*" from the root project
    expo-asset@"~11.1.7" from [email protected]
  peer react@"*" from [email protected]
  node_modules/expo-gl
    peerOptional expo-gl@">=11.0" from @react-three/[email protected]
    node_modules/@react-three/fiber
      peerOptional @react-three/fiber@"^8.17.9" from [email protected]
      node_modules/3d-tiles-renderer
        3d-tiles-renderer@"*" from the root project
  peer react@"^18.0.0 || ^19.0.0" from [email protected]
  node_modules/react-native-web
    peerOptional react-native-web@"*" from [email protected]
    node_modules/expo-gl
      peerOptional expo-gl@">=11.0" from @react-three/[email protected]
      node_modules/@react-three/fiber
        peerOptional @react-three/fiber@"^8.17.9" from [email protected]
        node_modules/3d-tiles-renderer
          3d-tiles-renderer@"*" from the root project

Could not resolve dependency:
peerOptional react@"^18.3.1" from [email protected]
node_modules/3d-tiles-renderer
  3d-tiles-renderer@"*" from the root project

If I'm understanding this correctly, recursively checking whether it's possible for any set of dependencies to be installed such that the set of range specifiers used for optional peer dependencies are satisfied in all versions of optionally installable packages seems like an unnecessary (and incredibly difficult) problem to solve. And one that doesn't need to be checked on initial install unless a real package that falls into the peer dependencies is installed by the user (directly or indirectly). My expectation, instead, would be that optional peer dependencies are not validated unless or until a real package is installed.

I expect that including a peer dependency semver range that supports react 19 will fix this conflict issue. It's fortunate that my package can support both versions but it will inevitably break when react 20 is released which will mean that old versions of 3d-tiles-renderer which used to install without error no longer will.

It would be great if someone from the npm team can confirm the behavior. I'm hoping this can be addressed because this makes an otherwise extremely useful npm feature very dangerous to use from a usability perspective.

gkjohnson avatar Jul 06 '25 01:07 gkjohnson

I expect that including a peer dependency semver range that supports react 19 will fix this conflict issue.

I've just published and tested a new version of the 3d-tiles-renderer packages with the peer dependencies set to include support for react 19 and there are no longer errors when installing the package, which aligns with what I've outlined in https://github.com/npm/cli/issues/8416#issuecomment-3040510099. However this likely means that this will break again when react@20 is released which is concerning. I'm hoping that this can be addressed in npm rather than having to do away with using optional peer dependencies entirely.

gkjohnson avatar Jul 13 '25 06:07 gkjohnson

I have same issue with transient optional peer dependencies. npm i runs successfully but npm ci reports error.

I use npm 11.6.2

example package.json

{
  "name": "npm-optional-peer-dependency",
  "type": "module",
  "dependencies": {
    "htmlnano": "2.1.5",
    "postcss-svgo": "7.1.0"
  }
}

svgo^3.0.2 is the optional peer dependency of htmlnano. svgo4.0.0 is dependency of postcss-svgo

  • No node_modules folder, no package-lock.json file. npm i installs the dependencies.
  • npm ci fails with error.

if you run npm i twice and after run npm ci then npm ci works. If you add the package.lock to a version control and stage it after the first run then you could compare the second npm i generates different lock file. The 2nd run adds the optional peer dependency to the package-lock.jon

ert78gb avatar Nov 15 '25 15:11 ert78gb