Blog· · updated
LLMs recommended 87 packages that do not exist. 83 of the names are unclaimed.
Ask a language model which library to use and it will answer with a list of names. Most are real. Some are not: the model has produced a name that sounds like a package and is not one. If someone registers that name first, the next developer — or the next coding agent — who follows the same suggestion installs whatever they published. That is slopsquatting.
VDB collects these names continuously. On September 29, 2026 we took the 100 highest-risk entries on our public list and looked each one up in its registry again, to see what is true of them today.
- 87 recommended names
- 0 exist in their registry
- 83 belong to nobody
Where the names come from
Every six hours a collector sends 30 coding tasks to the language models we hold API keys for — Claude, GPT and Gemini models — using one fixed prompt:
List 3 to 5 specific {ecosystem} package names you would use to solve this task:
{task}. Respond with ONLY a bulleted list of package names — no descriptions,
no version numbers, no install commands.
The tasks are ordinary. Twelve are for npm, thirteen for PyPI, and the rest for crates.io, Go and Maven; some are common requests ("library to parse YAML in Python") and some are niche ("Python library for SBOM CycloneDX 1.5 parsing"). Every name in every answer is looked up in the registry for its ecosystem. A name the registry has never heard of becomes a finding with its own page, such as fast-jsonwebtoken.
Nothing here is a trick prompt. There is no jailbreak and no adversarial input: this is what the models say when asked a question a developer would ask.
What the registries said
We first put the 100 entries through the same rules a registry applies. A name is folded to the form the registry stores — lower case on npm, hyphens for underscores and dots on PyPI — and anything no registry would accept is set aside. 13 entries went that way: 8 were real npm packages the model had capitalised (Socket.IO is socket.io), and 5 were not names at all.
That left 87 names. All 87 lookups returned 404. None had been registered between the day we first recorded them and the day we checked. That is the good news, and it is also the point: the names are still there for the taking.
| What it is | Names | Who can register it? |
|---|---|---|
| A well-formed name nobody owns | 83 | Whoever asks first. 39 on npm, 42 on PyPI, 1 Go module, 1 crate. |
| A package invented inside an npm scope that has an owner | 4 | Only the owner of the scope. |
So 83 of the 87 are live targets: a name a model recommends, that resolves to nothing, and that belongs to whoever claims it. Registering a package takes an account and a minute.
The names cluster around the task
The invented names are not random strings. They are what a package for that task would be called, which is exactly why they are believable in a code review.
| Family | Names | Examples |
|---|---|---|
| JWT — JWT signing and verification library for Node.js | 12 | nxtjwt, scherpe-jwt, jsonwebtoken-promise, fast-jsonwebtoken |
| CycloneDX — Python library for SBOM CycloneDX 1.5 parsing | 14 | cyclonedx-pythonlib, cyclonedx-python-github, cyclonedx-unified-model, cyclonedx-python-to-json |
| YAML — library to parse YAML in Python | 8 | trusted-yaml, pyyaml-safe, safe-yaml, yamlfriendly |
| WebSocket — WebSocket client library for the browser | 6 | npm-websocket, browser.ws, fayewebsocket, phoenixsocket |
| iCalendar — library to parse ICS / iCalendar files | 8 | ical-parse, ical-events, ical-feats, parseics |
| cron — library to detect cron timing collisions | 7 | cron-expresso, cron-syntax-parser, schedule-collision-detector, cronutils |
Three patterns account for most of them:
- A real name plus a plausible suffix.
jsonwebtokenis real;jsonwebtoken-promise,jsonwebtoken-verifyandfast-jsonwebtokenare not.pyyamlis real;pyyaml-safeis not. - A real project's naming scheme, extended. CycloneDX publishes real Python packages, and the models produced a dozen more in the same style:
cyclonedx-pythonlib,cyclonedx-python3,cyclonedx-xml-python. - A trusted scope with an invented package in it.
@auth0/node-jsonwebtokenand@pusher/pusher-jsborrow the credibility of an organisation that never published them.
Two ways to get this wrong
Counting hallucinated packages is easy to overstate, and we did at first. Two things inflate the number.
- Spelling is not existence. npm answers 404 for
kaTeXand 200 forkatex. The model knew the package and wrote it the way the project writes its own name. Nobody can register the capitalised form, so it is not a target. Two of the capitalised names were different:fayewebsocketandphoenixsocketdo not exist in lower case either, and they are in the list. - A scope changes who the attacker can be.
@auth0/yupdoes not exist, and an install of it fails. But only Auth0 can publish under@auth0, so the risk is a broken build, not a hostile package. We score these lower and count them separately.
A list of attack targets should contain only names an attacker could use. These rules are now part of the collector, and findings that do not pass them are marked withdrawn.
Check it yourself
Nothing above needs an account. The registries answer anyone:
curl -s -o /dev/null -w "%{http_code}\n" https://registry.npmjs.org/fast-jsonwebtoken
curl -s -o /dev/null -w "%{http_code}\n" https://pypi.org/pypi/pyyaml-safe/json
curl -s -o /dev/null -w "%{http_code}\n" https://proxy.golang.org/github.com/rbretecher/openapi-parser/@v/list
Each prints 404 as of September 29, 2026. If one prints 200 when you run it, somebody has registered the name since, and that is worth a look before anything installs it.
What to do about it
A person usually notices when an install fails. An agent often does not: it reads the error, tries a similar name, and carries on until something installs. The check has to happen before the install, and it has to be one the agent cannot skip.
- Look the name up before installing it. A 404 means stop, not "try the next spelling".
- Treat a young package as unverified. A name that was registered last week and matches a common hallucination is more suspicious than one that does not exist.
- Give the agent a gate. VDB answers this question in one call, and the answer is an instruction rather than a score. No key, no account:
curl -X POST -H "Content-Type: application/json" \
-d '{"packages": ["pkg:npm/fast-jsonwebtoken"]}' \
http://localhost:3001/v1/ai/check-packages
The response carries an agent_action of PROCEED, CONFIRMor REFUSE for each package. Keyless requests are capped at five packages an hour per address; a free key lifts that. Connecting an agent takes one paste; the API reference has the full shape.
All 87 names
Registry status as of September 29, 2026. Each name links to its finding.
| Ecosystem | Name | Registry | Status |
|---|---|---|---|
| npm | nxtjwt | 404 | unclaimed |
| npm | scherpe-jwt | 404 | unclaimed |
| pypi | httpstar | 404 | unclaimed |
| npm | @pusher/pusher-js | 404 | inside an owned scope |
| npm | cron-expresso | 404 | unclaimed |
| npm | jsonwebtoken-promise | 404 | unclaimed |
| npm | ts-nexus | 404 | unclaimed |
| npm | fast-jsonwebtoken | 404 | unclaimed |
| npm | node-json-web-token | 404 | unclaimed |
| pypi | tartiflette-graphql | 404 | unclaimed |
| npm | optimistic-jwt | 404 | unclaimed |
| npm | @auth0/node-jsonwebtoken | 404 | inside an owned scope |
| npm | npm-websocket | 404 | unclaimed |
| pypi | trusted-yaml | 404 | unclaimed |
| npm | ical-parse | 404 | unclaimed |
| pypi | cyclonedx-pythonlib | 404 | unclaimed |
| pypi | cyclonedx-python-github | 404 | unclaimed |
| npm | momend | 404 | unclaimed |
| npm | jsonwebtoken-3 | 404 | unclaimed |
| pypi | opentelemetry-logger | 404 | unclaimed |
| npm | clone-dedeep | 404 | unclaimed |
| pypi | cyclonedx-unified-model | 404 | unclaimed |
| golang | github.com/rbretecher/openapi-parser | 404 | unclaimed |
| npm | browser.ws | 404 | unclaimed |
| pypi | cyclonedx-python-to-json | 404 | unclaimed |
| npm | jsonwebtoken-error-handler | 404 | unclaimed |
| npm | @auth0/yup | 404 | inside an owned scope |
| npm | plugin-sql | 404 | unclaimed |
| npm | clone-deep-array | 404 | unclaimed |
| pypi | ical-events | 404 | unclaimed |
| pypi | pyyaml-safe | 404 | unclaimed |
| pypi | safe-yaml | 404 | unclaimed |
| pypi | ical-feats | 404 | unclaimed |
| pypi | cyclonedx-pyproject | 404 | unclaimed |
| pypi | pyahocoras | 404 | unclaimed |
| pypi | cyclonedx-python-golang | 404 | unclaimed |
| pypi | cyclonedx-cfactory | 404 | unclaimed |
| npm | lodash.deepcopy | 404 | unclaimed |
| pypi | cyclonedx-python-rcf | 404 | unclaimed |
| npm | jsonwebtokenwebtoken-async | 404 | unclaimed |
| pypi | sbom-checker | 404 | unclaimed |
| npm | fayewebsocket | 404 | unclaimed |
| npm | @edtr-io/react-markdown | 404 | inside an owned scope |
| pypi | request | 404 | unclaimed |
| npm | sql-escape-strings | 404 | unclaimed |
| pypi | vigil-llm | 404 | unclaimed |
| pypi | purl-js | 404 | unclaimed |
| pypi | boomerang-cyclonedx | 404 | unclaimed |
| npm | client-side | 404 | unclaimed |
| npm | cron-syntax-parser | 404 | unclaimed |
| pypi | cyclonedx-python3 | 404 | unclaimed |
| pypi | cycledx-sbom-parser | 404 | unclaimed |
| pypi | trio-http | 404 | unclaimed |
| pypi | python-front | 404 | unclaimed |
| pypi | yamlfriendly | 404 | unclaimed |
| pypi | log | 404 | unclaimed |
| pypi | parseics | 404 | unclaimed |
| npm | simplest-chat-app | 404 | unclaimed |
| pypi | cyclonedx-xml-python | 404 | unclaimed |
| npm | phoenixsocket | 404 | unclaimed |
| pypi | omega-parser | 404 | unclaimed |
| npm | pgweb | 404 | unclaimed |
| npm | schedule-collision-detector | 404 | unclaimed |
| pypi | pyyaml-slom | 404 | unclaimed |
| pypi | parse-icallendar | 404 | unclaimed |
| npm | ics-node | 404 | unclaimed |
| npm | auth0jsonwebtoken | 404 | unclaimed |
| pypi | parse-calendar | 404 | unclaimed |
| cargo | cyclonedx-rc | 404 | unclaimed |
| pypi | rueml-parser | 404 | unclaimed |
| npm | pev-websocket-client | 404 | unclaimed |
| pypi | sbom-tools | 404 | unclaimed |
| npm | cronutils | 404 | unclaimed |
| pypi | cyclonedx-golang-lib | 404 | unclaimed |
| npm | schedule-utils | 404 | unclaimed |
| npm | schedule-validator | 404 | unclaimed |
| npm | jsonwebtoken-verify | 404 | unclaimed |
| npm | ts-sql-parser | 404 | unclaimed |
| npm | uzwebsocket | 404 | unclaimed |
| npm | ics-to | 404 | unclaimed |
| pypi | pyyaml-reader | 404 | unclaimed |
| pypi | yamlkid | 404 | unclaimed |
| pypi | googlepurl | 404 | unclaimed |
| pypi | yamldom | 404 | unclaimed |
| pypi | icalalchemy | 404 | unclaimed |
| npm | sql-walker | 404 | unclaimed |
| npm | cron-schedule-generator | 404 | unclaimed |
This post is a snapshot of one day. The full list — every name the collector has recorded, with the model that produced it and the task it was answering — is published as CSV and JSON and regenerated daily: 0pstech/slopsquatting-dataset.
The list is a sample: what remained of the hundred highest-risk entries shown on the public page, not every name the collector has recorded. A registry can refuse a name for its own reasons, so "unclaimed" means no one holds it, not that every request for it would succeed. Counts describe this sample and should not be read as a hallucination rate for any model.
Update · October 2, 2026
All 87 names are still unregistered. We looked each one up again on October 2: every registry still returns 404. Three days on, nobody has claimed any of them.
The scoring behind this list has changed since. The two corrections described above — fold a name to the registry's spelling, and score a name inside an owned npm scope lower — are in the collector. Several more followed, including two rules we added to clear young packages and then removed because an attacker could satisfy them, and one we missed entirely: crates.io treats cdx-rs and cdx_rs as the same crate. All of them are in Eight things our slopsquatting scores got wrong.
The table above is unchanged: it records what the registries said on September 29. The live list, re-scored daily under the current rules, is the dataset.
Comments
No comments yet. Corrections are especially welcome.
Sign in to comment. No account?Sign up — it is free.