Skip to main content

A post doing the rounds on Hacker News this week describes something most development teams never do. The author of an open source project paid volunteers twenty five euro an hour to sit on a video call, share their screen, and try to follow his README out loud. He took notes by hand while strangers told him what his instructions failed to explain. About one hundred and fifty euro later he had a README that demonstrably worked.

It is a good method and more teams should steal it. But it assumes something that is not always true: that there is a README to follow in the first place.

So we checked. We took the 500 most downloaded packages on npm and asked the registry for each one’s README, the same way every tool, mirror, SBOM generator and coding agent asks for it. For 54 of them, the registry returned nothing at all.

TL;DR

  • 54 of the top 500 npm packages return an empty README from the registry API, including react, typescript, undici, axios and tailwindcss. Those 54 account for 4.69 billion weekly downloads between them.
  • 51 of those 54 ship a README inside the published tarball anyway. That is 499,460 bytes of documentation that exists in the artefact and is invisible to the API. Only 3 genuinely have no README anywhere.
  • 105 of the 446 packages that do return a README never show how to load themselves. No import, no require, no mention of their own name in any code block. 77 of the 500 contain no code block at all, and 23 of those do have a README.
  • 6 packages are ESM only and still tell you to require() them, led by cookie at 230 million downloads a week.
  • That require() instruction now produces three different outcomes depending on your Node version, and the quietest one is the worst: on Node 22.12 and later it succeeds and hands you an object where the documented method is undefined.

What we measured, and how

Everything below was collected first hand on 11 October 2026, on Node 22.22.0 and npm 10.9.4.

We built a candidate pool of 13,978 package names from 50 searches against the npm search API, then ranked them by last week downloads from npm’s own downloads endpoint. 7,191 of the 7,491 unscoped names returned figures; the remaining 300 were rate limited and are excluded. Scoped packages are out of scope here because the bulk downloads endpoint will not accept them, and we would rather state that than quietly mix two sampling methods.

For the top 500 we pulled the full packument, read the readme field, parsed every fenced code block out of it, and compared what the code blocks tell you to do against what the package manifest actually permits. Where the registry returned nothing, we downloaded the published tarball and looked inside it.

Finding one: the README that is not there

The npm registry serves a readme field on every package document. It is the field the ecosystem reads. For 54 of the top 500 it is empty or near empty.

This is not a long tail of abandoned utilities. It is react-is at 371 million weekly downloads, typescript at 307, esbuild at 303, undici at 215, node-fetch at 209, react at 188, react-dom at 177, tailwindcss at 136, axios at 118. Add the 54 together and you get 4,687,145,861 downloads a week against packages whose documentation the registry cannot hand you.

We then downloaded all 54 tarballs. 51 of them contain a README file. Axios ships a 107,633 byte README.md. Undici ships 33,753 bytes. Node-fetch ships 29,109. The documentation was written, committed, and published. It is sitting in the artefact your package manager already downloaded. The registry metadata simply does not carry it.

Only three of the 54 have no README file in the tarball either: eslint-module-utils, eslint-config-next and metro-babel-transformer. Those are the real documentation gaps. The other 51 are a plumbing failure, and a plumbing failure is worse, because nobody is embarrassed by it.

Why a missing field matters more in 2026 than it did in 2020

For years the answer to this was “so what, people read the GitHub page.” That answer is ageing badly.

The registry API is what non humans read. Dependency dashboards, licence and policy scanners, internal package mirrors, documentation aggregators, IDE hover cards and, increasingly, the coding agents your team runs all pull from the same endpoint we pulled from. When that endpoint returns an empty string, a human shrugs and opens GitHub. A pipeline records “no documentation” and moves on. An agent writes the integration from memory instead of from your instructions, and memory is where hallucinated APIs come from.

We tried to confirm what npm’s own website renders for these packages and could not: npmjs.com returns HTTP 403 to curl and to every reader proxy we tried. So we are claiming precisely what we measured, which is that the API has nothing, and nothing is what the ecosystem’s automated readers receive.

Finding two: 105 READMEs never show you how to load the thing

Among the 446 packages that do return a README, we looked for the single most basic instruction a package README can contain: a code block that names the package itself, in an import or a require.

105 of those 446 do not have one. 23 of them contain no fenced code block at all, and another 54 have no README to put a code block in, which puts 159 of the top 500 in the same practical position. These are not placeholder packages either; the cutoff for the top 500 sits at 15.3 million downloads a week.

This is the failure the Hacker News author paid people to find, except it does not need a volunteer or a video call. It needs one regular expression. If your README never names your package inside a code fence, every consumer is guessing at the entry point, and the guess that gets copied around the internet becomes the de facto documentation.

Finding three: six packages document an import that their own manifest forbids

This is the class we expected to be the headline, and the interesting part is why it is not.

We classified all 500 by module format from the manifest: 246 CommonJS only, 148 ESM only, 106 dual. Then we checked whether any ESM only package’s README still contains require('<its own name>'). Six do. The largest is cookie at 230 million weekly downloads, whose example section opens with var cookie = require("cookie") while cookie@2.0.1 declares "type": "module" and an exports field pointing at one ESM file, with no require condition anywhere. The others are formdata-polyfill, pathval, basic-auth, postcss-calc and postcss-colormin.

Three years ago that would have been a straightforward bug report. Today it is stranger. We installed them and ran the instructions.

$ node --no-experimental-require-module -e "require('cookie')"
Error [ERR_REQUIRE_ESM]: require() of ES Module
  /tmp/rtest/node_modules/cookie/dist/index.js not supported.

That is the old behaviour, still what you get on Node 22.11 and earlier. Now the same line on Node 22.22.0, where require(esm) is enabled by default:

$ node -e "console.log(Object.keys(require('cookie')))"
[ 'parseCookie', 'parseSetCookie', 'stringifyCookie', 'stringifySetCookie' ]

It works. The README is fine again, by accident, because Node changed underneath it. But the third outcome is the one worth your attention. require() of an ES module returns the module namespace, and Node deliberately does not unwrap a default export. So for any package whose public surface is a default export, the documented line succeeds and silently gives you the wrong object:

$ npm i chalk@6
$ node -e "const c = require('chalk'); console.log(typeof c.red)"
undefined
TypeError: c.red is not a function

Three Node versions, three outcomes, one unchanged line of documentation: a hard error, a correct result, and a silent wrong value. Chalk’s own README does not tell you to do this, which is why it is a useful control. The six packages that do tell you to do it are handing that lottery ticket to every reader.

What we would actually do about it

None of this needs a volunteer on a video call, which is the point. Human README testing is the right tool for the half of the problem that is about comprehension, ordering, unstated assumptions and bad jokes. It is the wrong tool for the half that a script settles in an afternoon. Run the cheap half first so you are not paying people to discover that your install command has a typo.

Four checks, in order of how much grief they save:

  • Fetch your own package from the registry API and assert the README field is not empty. One HTTP request, in CI, after every publish. If it comes back empty, your documentation does not exist as far as the ecosystem’s tooling is concerned.
  • Assert that your README contains at least one code fence naming your package. A five line test. 105 of the top 500 would fail it.
  • Extract every import and require from your own README and resolve them against your exports map. This is mechanical, and it catches documented subpaths that your own manifest refuses to serve.
  • Run your quickstart on the oldest Node version you claim to support, not just your dev machine. The require(esm) change means a passing test on 22.22 proves nothing about 22.11.

And if you consume packages rather than publish them, the inverse holds. When an agent or a junior developer integrates a dependency, the instructions they are working from may not be the instructions the maintainer wrote. Pin the version, read the exports map, and treat the README on a package page as a claim rather than a contract.

Method and limits

Pool of 13,978 names from 50 npm search queries, ranked by last week downloads; 300 unscoped names excluded as rate limited; scoped packages excluded by design. Top 500 analysed from full packuments pulled on 11 October 2026. Module format classified from type, main, module and the exports conditions rather than from guesswork. Runtime behaviour verified by real installs on Node 22.22.0 and npm 10.9.4. The 54 empty README cases were each confirmed against the published tarball. We did not measure whether npmjs.com renders a README for those 54, because it refuses automated clients, and we are not going to assert something we could not check.

The download ranking is a snapshot. The module format classification is not: once a package goes ESM only inside a major line, every README snippet written before that is a dated artefact, and nothing in npm flags it.

We build things that check themselves

REPTILEHAUS builds and maintains production systems for founders, agencies and in house teams, and this is the kind of work that tends to be invisible until it bites: publish pipelines that verify their own output, dependency and supply chain auditing, CI that tests the documentation as well as the code, and AI agents wired into systems where the inputs are actually trustworthy. If your packages, your docs or your release process have never been measured from the outside, that is usually a short engagement with a long payoff. Get in touch.

Related reading: we traced 35,319 npm releases and found 78 silent licence changes, using the same registry and the same approach.

📷 Photo by Brett Jordan on Unsplash