Skip to content

[BUG] npm install produces different results on first and second run #8535

Closed

Description

@liamcmitchell

Note:

Use npm >= 11.6.3 to avoid (these) issues!

More info in this comment: #8535 (comment)

Is there an existing issue for this?

  • I have searched the existing issues

This issue exists in the latest npm version

  • I am using the latest npm

Current Behavior

Discovered here: electron-userland/electron-builder#9259

A clean npm install produces different results on the first and second run.

Expected Behavior

npm install should produce the same results each time.

Steps To Reproduce

On mac, ignoring scripts just in case:

npm install -g npm@latest
mkdir test && cd test
npm init -y
npm install --ignore-scripts @abandonware/noble
# added 156 packages, and audited 157 packages in 972ms
npm install --ignore-scripts
# changed 12 packages, and audited 157 packages in 542ms

The second install shows "changed 12 packages" which is unexpected. This is also reproducible installing from package/lock files.

rm -rf node_modules
npm install --ignore-scripts
# added 156 packages, and audited 157 packages in 1s
npm install --ignore-scripts
# changed 12 packages, and audited 157 packages in 553ms

The different results are visible in the FS by diffing the output of find node_modules before and after.

Environment

  • npm: 11.5.2
  • Node.js: v22.14.0
  • OS Name: MacOS
  • System Model Name: Macbook Pro M4
  • npm config:
; node bin location = /Users/liam.mitchell/.local/share/fnm/node-versions/v22.14.0/installation/bin/node
; node version = v22.14.0
; npm local prefix = /Users/liam.mitchell/npm-install
; npm version = 11.5.2
; cwd = /Users/liam.mitchell/npm-install
; HOME = /Users/liam.mitchell

Activity

  1. liamcmitchell commented on Aug 27, 2025

    @liamcmitchell
    ContributorAuthor

    11.2 looks like it produces identical results between runs (expected behavior):

    rm -rf node_modules package.json package-lock.json
    npm install -g npm@11.2
    npm init -y
    npm install --ignore-scripts @abandonware/noble
    # added 12 packages, and audited 13 packages in 946ms
    find node_modules -name package.json > find1
    npm install --ignore-scripts
    # up to date, audited 13 packages in 816ms
    find node_modules -name package.json > find2
    diff find1 find2
    # no diff
    rm -rf node_modules
    npm install --ignore-scripts
    # added 12 packages, and audited 13 packages in 765ms
    find node_modules -name package.json > find1
    npm install --ignore-scripts
    # up to date, audited 13 packages in 656ms
    find node_modules -name package.json > find2
    diff find1 find2
    # no diff
  2. TillFProtzek commented on Aug 27, 2025

    @TillFProtzek

    Happens for us too. It even breaks our pipeline, because the second version that is generated will fail on npm ci with an package-lock out of sync error.

  3. liamcmitchell commented on Aug 27, 2025

    @liamcmitchell
    ContributorAuthor

    I've been trying to follow the code. From what I can see so far, the ideal tree looks the same in both runs.

    Noticed that the 12 changed packages are marked because they are missing their .bin/ files:

    const binsExist = ideal.binPaths.every((path) => existsSync(path))

    So these files aren't created on first install?

  4. alexsch01 commented on Aug 27, 2025

    @alexsch01
    Contributor

    @liamcmitchell I believe my issue #8409 is the reason for this difference on all platforms

  5. liamcmitchell commented on Aug 27, 2025

    @liamcmitchell
    ContributorAuthor

    Happens for us too. It even breaks our pipeline, because the second version that is generated will fail on npm ci with an package-lock out of sync error.

    @TillFProtzek I don't see a difference in package lock in my testing.

  6. liamcmitchell commented on Aug 27, 2025

    @liamcmitchell
    ContributorAuthor

    Those 12 bin flies are missing on first install because they were on the trash list:

    async #createBinLinks (node) {
    if (this[_trashList].has(node.path)) {
    return

  7. liamcmitchell commented on Aug 27, 2025

    @liamcmitchell
    ContributorAuthor

    They are on the trash list because one optional dep failed (expected, unsupported platform) and optionalSet() determined that 144 other packages were related:

    // if the node is optional, then the failure of the promise is nonfatal
    // just add it and its optional set to the trash list.
    [_handleOptionalFailure] (node, p) {
    return (node.optional ? p.catch(() => {
    const set = optionalSet(node)
    for (node of set) {
    log.verbose('reify', 'failed optional dependency', node.path)
    node.ideallyInert = true
    this[_addNodeToTrashList](node)

  8. TillFProtzek commented on Aug 28, 2025

    @TillFProtzek

    @liamcmitchell I had both occurences. Installs/deletions without any change to the package-lock, and some with changes to the package-lock. Regressed like you said to 11.2.0 and we have no problems anymore.

  9. liamcmitchell commented on Aug 28, 2025

    @liamcmitchell
    ContributorAuthor

    The 144 marked packages look valid, most of the deps are brought in by the unsupported platform dep.

    Mermaid diagram shows marked packages in red, dotted lines show optional links.

    It looks like there are a few deps that weren't marked and should have been. They are all optional dependencies themselves.

    Image Image Image

    If I understand the graph correctly, I should only have 6 packages installed on my platform 🤔

  10. oldium commented on Sep 9, 2025

    @oldium
    Contributor

    I think I just faced the same issue.

    I have @swc/core as a dependency of one directly installed package (@swc-node/jest) and it has optional platform dependencies, which are installed on the clean npm install (no node-modules, no package-lock.json), but they are removed when running the npm install the second time. They are also removed when I run npm update --save.

    #> npm install
    ...
    added 1128 packages, and audited 1129 packages in 1m
    ...
    
    #> npm install
    ...
    added 1 package, removed 11 packages, and audited 1119 packages in 2s
    ...
    

    This is 100% reproducible. I have the following dependencies in my packages.json:

      "dependencies": {
        "@azure/msal-node": "^3.7.3",
        "@microsoft/microsoft-graph-client": "^3.0.7",
        "clsx": "^2.1.1",
        "connect": "^3.7.0",
        "cookies-next": "^6.1.0",
        "cors": "^2.8.5",
        "crypto-random-string": "^5.0.0",
        "dirty-json": "^0.9.2",
        "email-addresses": "^5.0.0",
        "express": "^5.1.0",
        "extensionless": "^1.9.9",
        "geist": "^1.5.1",
        "html-react-parser": "^5.2.6",
        "ipaddr.js": "^2.2.0",
        "iron-session": "^8.0.4",
        "is-base64": "^1.1.0",
        "localenv": "^0.2.2",
        "lodash": "^4.17.21",
        "lucide-react": "^0.542.0",
        "marked": "^16.2.1",
        "next": "^15.5.2",
        "nocache": "^4.0.0",
        "node-cache": "^5.1.2",
        "node-html-parser": "^7.0.1",
        "p-limit": "^7.1.1",
        "react": "19.1.1",
        "react-dom": "19.1.1",
        "react-hot-toast": "^2.6.0",
        "smtp-server": "^3.14.0",
        "sqlite": "^5.1.1",
        "sqlite3": "^5.1.7",
        "striptags": "^3.2.0",
        "throat": "^6.0.2",
        "tiny-typed-emitter": "^2.1.0",
        "winston": "^3.17.0",
        "yn": "^5.1.0"
      },
      "devDependencies": {
        "@eslint/js": "^9.35.0",
        "@next/bundle-analyzer": "^15.5.2",
        "@swc-node/jest": "^1.9.1",
        "@swc-node/register": "^1.11.1",
        "@tailwindcss/postcss": "^4.1.13",
        "@types/cors": "^2.8.19",
        "@types/express": "^5.0.3",
        "@types/is-base64": "^1.1.3",
        "@types/jest": "^30.0.0",
        "@types/lodash": "^4.17.20",
        "@types/node": "^24.3.1",
        "@types/react": "^19.1.12",
        "@types/react-dom": "^19.1.9",
        "@types/smtp-server": "^3.5.12",
        "@vercel/nft": "^0.30.1",
        "eslint": "^9.35.0",
        "eslint-config-next": "15.5.2",
        "jest": "^30.1.3",
        "postcss": "^8.5.6",
        "selfsigned": "^3.0.1",
        "tailwindcss": "^4.1.13",
        "typescript": "^5.9.2",
        "typescript-eslint": "^8.43.0"
      }
    
  11. oldium commented on Sep 9, 2025

    @oldium
    Contributor

    This is a minimal package.json case to see removed optional dependencies:

    {
      "devDependencies": {
        "@swc-node/jest": "^1.9.1"
      }
    }
    

    This is a minimal package.json to see also node_modules/@swc/helpers moved under node_modules/next/node_modules/@swc/helpers, because of the removal:

    {
      "dependencies": {
        "next": "^15.5.2"
      },
      "devDependencies": {
        "@swc-node/jest": "^1.9.1"
      }
    }
    
  12. AviVahl commented on Sep 14, 2025

    @AviVahl

    This bug is super annoying. I had it happen for another transitive optional dependency of electron-builder- @electron/windows-sign
    See #8563 (duplicate).

  13. added a commit that references this issue on Sep 23, 2025
  14. oldium commented on Sep 26, 2025

    @oldium
    Contributor

    The results are better with npm 11.6.1 (optional dependency does not get removed), but the following example:

    {
      "dependencies": {
        "next": "^15.5.2"
      },
      "devDependencies": {
        "@swc-node/jest": "^1.9.1"
      }
    }
    

    still gives unstable results for peer @swc/helpers.

  15. oldium commented on Sep 26, 2025

    @oldium
    Contributor

    This is quite annoying, because the npm ci run during Docker build fails because of this dependency instability:

    npm error `npm ci` can only install packages when your package.json and package-lock.json or npm-shrinkwrap.json are in sync. Please update your lock file with `npm install` before continuing.
    npm error
    npm error Missing: @swc/helpers@0.5.15 from lock file
    

    The npm install has to be run twice in order to npm ci succeed.

  16. AviVahl commented on Sep 27, 2025

    @AviVahl

    the issue is mostly fixed for me after #8579 (included in npm@11.6.1)

    @owlstronaut I still see a tiny 1 line difference that adds "peer": true on a second npm i run of https://github.com/AviVahl/example-electron-application

    Steps to reproduce:

    git clone git@github.com:AviVahl/example-electron-application.git
    cd example-electron-application
    rm package-lock.json
    npm i
    git add .
    git clean -fdx
    npm i
    git --no-pager diff
    
  17. added a commit that references this issue on Oct 13, 2025
  18. liamcmitchell commented on Dec 8, 2025

    @liamcmitchell
    ContributorAuthor

    Summary of recent npm install/package-lock issues:

    • 11.3.0 added #8184 which fixes unexpected package-lock removals in many cases. This exposed my issue above.
    • 11.5.0 added #8431 which incorrectly prunes peer & optional deps, causing unexpected package-lock removals.
    • 11.6.1 added #8579 which attempts to fix #8431 by incorrectly changing peer calc logic, causing unexpected package-lock changes and removals.
    • 11.6.2 added #8537 which fixes optional set calc and #8602 which fixes inert-calc/trash/omit order. These fixes should have fixed my original issue but then I ran into #8431 and #8579.
    • 11.6.3 added #8645 which reverts the problematic #8431 and #8579 and fixes the original issue by rewriting dep flag calc. This will cause unexpected package-lock changes and additions if package-lock was touched by 11.5.0-11.6.2. At this point I was happy that my npm install was reproducible and stopped investigating further.
    • 11.10.1 added #8981 which fixes resolution of optional peer deps. Invalid optional peer deps will be ignored or corrected according save mode.

    Use npm >= 11.10.1 to avoid the above issues!

  19. pinned this issue on Dec 8, 2025
  20. wraithgar commented on Dec 8, 2025

    @wraithgar
    Contributor

    Thank you @liamcmitchell I edited the issue to include your summary and a link to that comment, and am going to pin this issue for the time being.

  21. Saibamen commented on Feb 19, 2026

    @Saibamen
    Contributor

    @liamcmitchell please update your comment about NPM version 11.10.1 and #8981

  22. unpinned this issue on Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Bugthing that needs fixingNeeds Triageneeds review for next steps

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions