'npm left-pad' Incident

I’m going to reproduce the famous npm left-pad incident. I love it when bugs have wikipedia pages. TLDR: a maintainer removed a library called left-pad from NPM breaking any project attempting to install it.

Why did this create chaos?

Popular projects like React and Babel didn’t explicitly import left-pad but pulled it indirectly through their dependency trees. Here is a visual of my project’s dependency tree:

me@MacBookPro ~/R/l/app> npm ls -a
app@1.0.0 /Users/me/Repos/leftpad-repro/app
└─┬ mid-lib@1.0.0
  └── tiny-pad@1.0.0

mid-lib is acting like React or Babel and tiny-pad is acting like left-pad. Instead of using npm as a registry, I created a directory called packages. For each package, I ran npm pack to create a tarball simulating a published package. All that mid-lib does is call tiny-pad:

// packages/mid-lib/index.js
const pad = require("tiny-pad");

module.exports = function formatId(n) {
  return pad(n, 5, "0");
};

// packages/mid-lib/package.json
{
    "name": "mid-lib",
    "version": "1.0.0",
    "main": "index.js",
    "dependencies": {
      // the file I got after running `npm pack`
      "tiny-pad": "file:../tiny-pad/tiny-pad-1.0.0.tgz"
    }
}

Similarly, here is tiny-pad:

module.exports = function tinyPad(str, len, ch = " ") {
    str = String(str);
    if (str.length >= len) return str;
    return ch.repeat(len - str.length) + str;
  };

As is, the project runs as expected:

me@MacBookPro ~/R/l/app (main)> npm install
added 2 packages, and audited 3 packages in 472ms
found 0 vulnerabilities
me@MacBookPro ~/R/l/app (main)> node index.js
00007

But what happens if I remove tiny-pad-1.0.0.tgz and run npm install at the top level?

me@MacBookPro ~/R/l/app (main)> rm ../packages/tiny-pad/tiny-pad-1.0.0.tgz
me@MacBookPro ~/R/l/app (main)> npm install

up to date, audited 3 packages in 496ms

found 0 vulnerabilities
me@MacBookPro ~/R/l/app (main)> node index.js
00007

Absolutely nothing! I admit: that was a trick question. package.json hasn’t changed, so it aligns with package-lock.json and /node_modules so npm does nothing. To trigger the error, we need a fresh install:

me@MacBookPro ~/R/l/app (main)> rm -rf node_modules package-lock.json
me@MacBookPro ~/R/l/app (main)> npm install
npm warn tarball tarball data for tiny-pad@file:../tiny-pad/tiny-pad-1.0.0.tgz (null) seems to be corrupted. Trying again.
npm warn tarball tarball data for tiny-pad@file:../tiny-pad/tiny-pad-1.0.0.tgz (null) seems to be corrupted. Trying again.
npm warn tarball tarball data for tiny-pad@file:../tiny-pad/tiny-pad-1.0.0.tgz (null) seems to be corrupted. Trying again.
npm warn tarball tarball data for tiny-pad@file:../tiny-pad/tiny-pad-1.0.0.tgz (null) seems to be corrupted. Trying again.
npm error code ENOENT
npm error syscall open
npm error path /Users/me/Repos/leftpad-repro/app/node_modules/tiny-pad/tiny-pad-1.0.0.tgz
npm error errno -2
npm error enoent ENOENT: no such file or directory, open '/Users/me/Repos/leftpad-repro/app/node_modules/tiny-pad/tiny-pad-1.0.0.tgz'
npm error enoent This is related to npm not being able to find a file.
npm error enoent
npm error A complete log of this run can be found in: /Users/me/.npm/_logs/2026-02-08T22_54_18_238Z-debug-0.log

This is exactly what happened with the left-pad incident. mid-lib hasn’t changed but a fresh install will try to pull a non-existent tiny-pad.

npm has made changes so that the rug can’t be pulled, but it’s still a great lesson. I learned a bunch reproducing this retro bug.