9.8left-pad unpublished from npm
A developer took all his packages off npm after a dispute over a name, and one of them, a few lines that pad a string, broke installs across JavaScript for two and a half hours.
- 1Read
- Blast radius
- Every npm install that needed left-pad 0.0.3, directly or through a chain of dependencies. npm says it impacted many thousands of projects, and names babel and atom, which pulled it in through a package called line-numbers.
- Duration
- 2.5 hours by npm's account, from shortly after 2:30 PM Pacific Daylight Time (PDT) on Tuesday 22 March 2016 until the original version was back by 4:55 PM.
- 2Follow
What happened, in order
- Mar 2016
Kik, the messaging company, and Azer Koçulu correspond over his npm package called kik and cannot agree. Kik asks npm for help.
- 18 Mar 2016
Under its dispute policy, npm decides the package name kik should be maintained by Kik and tells both parties. Under that policy the existing package would normally have stayed on the registry.
- 22 Mar, ~2:30 PM PDT
Koçulu has unpublished kik and 272 other packages, one of them left-pad. Shortly after 2:30 PM npm starts seeing hundreds of failures a minute as dependent projects, and their dependents, fail to fetch it.
- Within 10 minutes
Another developer publishes a functionally identical left-pad as version 1.0.0. Errors continue, because line-numbers asks for exactly 0.0.3.
- 4:05 PM PDT
npm announces that, at the request of the new owner, it will re-publish the original 0.0.3, which needs a restore from backup because re-publishing a version is otherwise impossible.
- 4:55 PM PDT
left-pad 0.0.3 is back on the registry, and npm asks users to clear their cache before installing again.
- 23 and 29 Mar
npm publishes its account of the incident, then a new unpublish policy.
- Mar 2016
- 3Find
Root cause
NoteThe registry let an author remove any version of any package at any time, however many other packages depended on it. Unpublishing left-pad took version 0.0.3 out of the registry, so every install that resolved it failed, and with it every package above it in the tree.
left-pad was tiny. The 0.0.3 file is 17 lines, 11 of them not blank: a loop that adds a character to the front of a string until it reaches a length. It sat deep in the tree. line-numbers depended on exactly version 0.0.3, and babel and atom depended on line-numbers, so most of the people whose builds broke had never chosen left-pad.
The quick fix could not work. A functionally identical 1.0.0 appeared within ten minutes, but a dependency pinned to exactly 0.0.3 does not accept 1.0.0, and the registry did not let anyone publish a version number again once it had existed. Only npm could put the original back, from a backup.
Removing one small leaf failed every install above it, and an exact pin on 0.0.3 refused the identical 1.0.0 published ten minutes later. - 4Trace
Why it got through
Nothing between an author and the registry asked whether a removal would break anyone. npm's dispute decision would have left the old kik package where it was, and npm's own account says it was the abrupt unpublishing, not the dispute resolution, that broke the builds. The missing safeguard was a check on dependents before removal, which is what npm added a week later.
On the consuming side, builds fetched every dependency from the registry at install time and trusted it to be there. An exact pin protected an install from new versions but not from an old one disappearing, and a tool that pulled a few lines of code through a chain of other packages carried the risk of every author in that chain.
- 5Check
The lesson
Every dependency is a promise someone else has to keep, and a transitive one is a promise you never agreed to. Know what your tree pulls in, keep your own copy of what you build with, and weigh a tiny dependency against copying a few lines.
- 6Change
What changed afterwards
On 29 March 2016 npm changed its policy. A version less than 24 hours old could still be unpublished; anything older needed npm support, who would refuse if removing it would break other installs. A package with every version removed would be replaced by a security placeholder package.
The policy was tightened again on 30 January 2020. Today a package can be unpublished within 72 hours if nothing else in the public registry depends on it, and after that only if nothing depends on it, it had fewer than 300 downloads in the last week and it has a single owner. left-pad itself is now deprecated, with a notice pointing to String.prototype.padStart().
- 7Verify
Where the sources differ
- The often quoted 273 packages is arithmetic: npm says kik "and 272 other packages".
- Many accounts say left-pad broke React. npm's post names babel and atom, and Kik's own post says its builds broke through JSCS; no source used here names React.
- left-pad is usually called 11 lines. The 0.0.3 file has 17 lines, 11 of them not blank.
- Koçulu's post says a patent lawyer contacted him. Kik's post, which publishes the email thread, says the sender was its patent agent, not a lawyer.
- 8Source
Sources
- Primarynpm, kik, left-pad, and npm (23 March 2016)
- Primarynpm, changes to npm's unpublish policy (29 March 2016)
- PrimaryAzer Koçulu, I've Just Liberated My Modules (archived copy)
- PrimaryMike Roberts (Kik), A discussion about the breaking of the Internet (archived copy)
- Analysisnpm Docs, npm Unpublish Policy
- DiscussionHacker News, NPM and Left-Pad: Have We Forgotten How to Program?
- 9Go