2026-09-11 · 5 min read

Slopsquatting, a year on: the models hallucinate less, but five of them invent the same package names

Last summer we wrote about slopsquatting, the attack where a model hallucinates a package name, an attacker registers it, and the next developer to accept the same suggestion installs the attacker's code. At the time the evidence was one big study and one proof-of-concept package with 30,000 downloads. A year on, the frontier models hallucinate a lot less, someone re-ran the study on them anyway, and the interesting result isn't the rate. It's that five different models invent the same names, and 53 of those names were free to register in April.

The rate fell, the threat didn't

The original USENIX study tested sixteen models and found 19.7% of generated package references pointed at packages that don't exist, across 576,000 code samples, with 205,474 unique made-up names. Commercial models were around 5%, open-source ones over 20%. That was 2024-era models. In May, an independent researcher re-ran the same prompts and validation code against the 2026 cohort: Claude Sonnet 4.6 and Haiku 4.5, GPT-5.4-mini, Gemini 2.5 Pro, DeepSeek V3.2. Nearly 200,000 paired Python and JavaScript prompts, checked against the full PyPI and npm package lists. Overall hallucination rates came in between 4.62% and 6.10%.

So the headline number roughly quartered, which is good and expected. The finding that matters is further down: 127 package names were generated identically by all five models. Not similar, identical. The models are trained on overlapping data and reach for the same plausible-sounding name when the real one escapes them, so a hallucinated name works less like random noise and more like a forecast of what thousands of developers will be told to install. When Socket checked the registries in April, 53 of those 127 names were unregistered, 41 on PyPI and 12 on npm. Anyone could have taken them. PyPI's security team then blocked 68 candidate names through their automated defenses, which is the first registry-side countermeasure I've seen documented for this specific attack.

Package hallucination rate fell from 19.7 percent in the 2024 study to 4.6 to 6.1 percent on 2026 frontier models, but 127 names were invented identically by all five models and 53 were unregistered.

Two real ones from this year

Aikido's February write-up has two cases that are more instructive than the original proof of concept, because neither one was a researcher's demo.

The first is unused-imports on npm. The real package is eslint-plugin-unused-imports; models drop the prefix. Someone registered the short name, npm eventually put it on security hold, and as of early February it was still pulling 233 downloads a week. Held, flagged, and still being installed a couple of hundred times weekly by tooling and people who accepted a suggestion without looking.

The second is the one I'd put in front of any team using coding agents. react-codeshift doesn't exist; it's a conflation of jscodeshift and react-codemod. The name originated inside AI-generated agent skill files, the SKILL.md instructions people share for Claude Code and similar tools, and from there it spread through forks. By the time an Aikido researcher claimed the package name defensively, it had propagated to 237 repositories and been translated into Japanese. Nobody hallucinated it 237 times. One agent hallucinated it once, into an instruction file, and then humans copied the instruction file. The supply chain for hallucinated names now includes the supply chain for prompts.

What this looks like in a diff

The review question hasn't changed since last year, but the diff it applies to has, because it's no longer only package.json. Here's the version in our library, which reconstructs the shape of the 2018 event-stream attack rather than a slopsquat, but the reviewer's move is identical: a tiny, plausible dependency added for something the platform already does.

Zero-pad invoice numbers for the finance exports · package.json, src/billing/invoiceNumber.js -1 +4
12 "dependencies": {
13 "express": "^4.19.2",
14+ "left-pad": "^1.3.0",
15 "pg": "^8.12.0"
1+const leftPad = require('left-pad');
8- return `${INVOICE_PREFIX}-${seq}`;
8+ return `${INVOICE_PREFIX}-${leftPad(String(seq), 8, '0')}`;
What a trained eye flags here

Three things, in order of how fast they should come to mind. One, String.prototype.padStart has been in the language since ES2017 and this service pins Node 20, so the dependency buys nothing; npm's own deprecation notice for left-pad says "use String.prototype.padStart()." Two, the project's stated rule forbids adding a dependency for anything the standard library does. Three, and this is the one that generalises to slopsquatting: a dependency is code that executes with the billing service's credentials on every import, may run install hooks in CI, and pulls in whatever ^1.3.0 resolves to next month. For a name you've never heard of, the first thing to check is not what it does but whether it exists, who published it, and when. For a name you have heard of, check it's spelled the way the registry spells it.

Review the PR yourself; it takes about two minutes and the canonical answer walks through the trust boundary argument properly.

The review reflex, updated for agents

Last year's advice was: every new line in a dependency manifest gets thirty seconds on the registry page. Still true. Two additions for 2026.

First, the manifest isn't the only place a package name enters your repo anymore. Agent instruction files, setup scripts a model wrote, README install commands, Dockerfiles: the react-codeshift case spread through none of the places a dependency scanner looks. If an AI-written PR includes a document that tells the next agent what to install, that document is a dependency manifest and deserves the same thirty seconds.

Second, the fact that models converge on the same wrong names cuts both ways. It's what makes the attack viable, and it's also what makes the defence cheap: the names are predictable, the lists are published, and a reviewer who has seen the pattern once, a real name minus its prefix, two real names fused together, a Python package that only exists in npm, will recognise the shape in the diff without needing to know the specific name. That's the whole pitch for practising on planted ones. The attacker is betting you'll accept a plausible name at speed. Speed is a habit, and so is stopping.

Try a review

A tiny, plausible dependency for something the platform already does.

Review the PR and 200+ others with planted bugs. Free, no signup. Canonical review after your verdict.

Review this PR →
Read next Slopsquatting: the attack your code review should catch
← All posts