Typosquatting depends on a person mistyping a real name. Package hallucination removes the person. The model produces a plausible name with confidence, the name resolves to nothing, and that empty space in a public registry is available to whoever claims it first. If the same name comes up again for the next developer, or the next agent, the claimant gets their code installed without having to trick anyone.
Andrew Nesbitt described slopsquatting as the case where an LLM hallucinates a non-existent package name and a bad actor registers it maliciously, and he credited Seth Larson with the name in April 2025 [3]. The word is new; the mechanism is older. Public registries such as PyPI and npm let anyone publish under any name that is not taken, which is what makes them useful and what makes an invented name exploitable.
Three properties decide whether a hallucination is a curiosity or an attack surface. The name has to be unregistered when the model first invents it. It has to recur, so that registering it pays off across many victims. And something has to install it without checking. The research below measured the first two. Coding agents supply the third.
What the measurements actually say
Spracklen and colleagues tested 16 code-generating models (three commercial GPT models and 13 open-source variants) on Python and JavaScript prompts drawn from Stack Overflow questions and package descriptions, producing 576,000 code samples [1]. They checked every recommended package against master lists of PyPI and npm names taken on January 10, 2024. Of roughly 2.23 million package recommendations, 440,445 named packages that did not exist, which is 19.7%, and those covered 205,474 unique invented names [1].
On average, 5.2% of the packages commercial models recommended did not exist, against 21.7% for open-source models, across 576,000 code samples in two programming languages that produced 205,474 unique hallucinated package names (Spracklen et al., USENIX Security 2025).
The gap between model types is large, but even the best result is not zero. The commercial models averaged at least 5.2% and the open-source models 21.7%, with GPT-4 Turbo lowest at 3.59% [1]. A rate of a few percent sounds small until you multiply it by how many dependency suggestions an engineering organization's agents make in a week.
The finding that matters most for attackers is persistence. The authors took 500 prompts that had produced hallucinations and re-ran each one ten times. In 43% of cases the same invented name appeared in all ten runs, 39% never reappeared, and 58% reappeared more than once [1]. An attacker does not need to predict a random error. They need to run popular prompts, collect the names that keep coming back, and register those.
| Finding | Reported figure | Defensive reading |
|---|---|---|
| Overall hallucinated share | 440,445 of about 2.23 million recommended packages (19.7%) | Invented names are a routine output, not a rare failure, so the control must sit at install time. |
| Commercial vs open-source average | At least 5.2% vs 21.7% | Model choice changes the rate but never removes it; do not treat a better model as a control. |
| Unique invented names | 205,474 | Too many to pre-register defensively; an allowlist of known names scales better than a blocklist of bad ones. |
| Repeat behavior on re-query | 43% in all 10 runs, 58% more than once, 39% never | Recurring names are predictable targets, which is what makes registration worthwhile for an attacker. |
| Distance from a real name | 48.6% at Levenshtein distance 6 or more | Most invented names are not near-misses of real ones, so typosquat similarity checks will miss them. |
| Cross-ecosystem confusion | 8.7% of hallucinated Python names were valid npm packages | A name that is real in one registry can be fake in another; check the ecosystem, not just the string. |
| Self-detection | Three of four tested models detected their own hallucinations with accuracy above 75% | Asking the model to double-check helps, but it is advice to the model, not enforcement. |
Two caveats keep these numbers honest. The models were selected from a January 2024 leaderboard, and today's models will have different rates; we do not know them and will not guess. The study also measured recommendations, not installs or attacks. What it established is that invented names are common and that many of them repeat, which is the precondition an attacker needs.
The experiment that showed people install them
Bar Lanyado of Lasso Security tested whether hallucinated names get installed in practice [2]. Models repeatedly suggested a Python package called huggingface-cli, which matches the name of Hugging Face's command-line tool but is not the name of the package that provides it. Lanyado published an empty package under that name, along with an unrelated dummy package to estimate how many downloads came from automated scanners rather than people.
He reported more than 30,000 authentic downloads in about three months, and found the name in installation instructions in a research repository README belonging to Alibaba [2]. The package did nothing, so the harm was zero. The point is that the hallucinated name had already escaped the chat window and entered documentation, where both humans and agents would copy it again. A name that starts as a model error can end up as a documented dependency.
Why agents turn a bad suggestion into code execution
In a chat interface, a person sits between the suggestion and the install. They might notice an unfamiliar name, search for it, or see a strange project page. A coding agent with a terminal tool removes that pause. It writes the import, runs the test, sees ModuleNotFoundError, and runs pip install to fix it, often in the same turn and often without showing the name to anyone.
Five steps connect a model's invented name to code running on your machine. Three checkpoints (an allowlisted registry, an age and popularity gate, and an install that asks first) each break the chain on their own.
Installation is itself code execution. pip's documentation says that installing from distributions involves running arbitrary code unless you restrict it, and recommends hash checking and binary-only installs as the countermeasures [5]. npm runs a package's preinstall, install and postinstall scripts during npm install [7]. An attacker who registers a hallucinated name does not need you to import their module. The install step alone runs their code with the agent's privileges.
Those privileges are usually generous. An agent working in a developer's checkout can read environment variables, cloud credentials in the home directory, SSH keys and the repository's tokens. An agent in CI can often read deployment secrets. The blast radius of a hallucinated install, meaning everything the installing process can reach, is whatever that agent can reach.
A worked example: the overnight dependency
Consider an illustrative company, Larkfield Analytics, that runs a coding agent overnight against its backlog. One ticket asks for table extraction from vendor PDF invoices. The agent drafts a parser that imports a package we will call invoicetable-parse, a name we made up for this example. The name sounds right, the code around it is plausible, and the first test run fails because the module is missing.
The agent responds the way it was built to: it runs pip install invoicetable-parse. Suppose an attacker registered that name two weeks earlier after seeing it recur in their own prompting. The package ships only as a source distribution, so pip builds it locally and its setup code runs. The setup code reads the CI environment, finds a cloud deployment key, and sends it to a remote host. The parser then works, because the attacker vendored a real PDF library underneath, and the agent opens a pull request with passing tests.
Nothing in that pull request looks wrong to a reviewer in the morning. The requirements file gains one line. The code is reasonable. The theft happened during the build, hours before any person looked. This is why reviewing the agent's output is not enough: the consequential action was the install, and it happened mid-session.
Where each checkpoint would have stopped it
- Allowlisted registry: if the CI runner can resolve packages only through an internal proxy that mirrors approved names, the install fails with not-found and the agent has to ask for the dependency.
- Age and popularity gate: a package published two weeks ago with almost no download history fails a minimum-age rule, and pnpm, for example, now ships a minimumReleaseAge setting that refuses versions newer than a set number of minutes [6].
- Install asks first: if the agent's role treats package installation in CI as an action that asks, the install waits in an approval queue with the package name visible, and a person who has never heard of invoicetable-parse declines it.
- Binary-only installs: with pip's --only-binary option the source distribution is refused, so its setup code never runs, although a malicious wheel would still install [5].
Registry and toolchain controls you own
The strongest control is to make public registries unreachable from where agents run, and to resolve every package through a proxy you operate. A proxy that serves only approved names turns a hallucination from a supply-chain event into a build error. A proxy that passes through to the public registry but logs every miss and every first-time name still gives you the signal you need to investigate.
- Commit lockfiles and install from them in CI. pip's hash-checking mode (--require-hashes) refuses any artifact whose hash is not already recorded, so an agent cannot add a new package without changing a reviewed file [5].
- Prefer binary-only installs where your dependency set allows it, so source-distribution build scripts do not run during install [5].
- Set a minimum release age for new versions and new packages. pnpm added minimumReleaseAge in v10.16.0, measured in minutes, with a per-package exclusion list for internal packages you trust [6].
- Add a popularity and provenance check for first-time dependencies: download history, linked source repository, maintainer age, and whether the name is real in the ecosystem you are installing into rather than another one.
- Disable dependency lifecycle scripts by default and allow them for named packages that need native builds, since npm runs install scripts from dependencies [7].
- Check new dependencies against the OpenSSF Malicious Packages database, which publishes reports of malicious packages from open-source registries in OSV format [4]. It catches known-bad packages, not a name registered yesterday.
Notice what is missing: defensive pre-registration of hallucinated names. With 205,474 unique invented names in one study alone [1], you cannot claim them all, and registries discourage squatting. Allowlisting known names scales; blocklisting invented ones does not.
Treat the install as a consequential action at the gateway
Registry controls work when every agent runs inside infrastructure you configured. Many do not: developers run coding agents on laptops with their own package manager settings. A control at the point where the agent asks its tools to act covers both cases, because the install command has to pass through it.
The useful unit is the action, not the tool name. A terminal tool called run_command tells you nothing; its argument might be ls or pip install. A gateway that normalizes shell commands reads the argument, splits compound commands joined with && or semicolons, looks inside command substitutions, and recognizes piped downloads such as curl | sh. Once it sees pip install or npm install as a distinct action, policy can treat it differently from a test run.
Consequence tiering then decides what happens. An install from a lockfile in a development environment may run. An install that adds a new package, or any install in CI or production, can ask a person and show them the package name and the session that requested it. The approver sees a one-line question that a reviewer would never have seen in the pull request: should this agent install a package called invoicetable-parse?
Detection: the signature of a slopsquat
A slopsquat leaves a recognizable trail if you log package resolution. The strongest single signal is a name that failed to resolve in your proxy logs and later resolved, because that is the moment someone registered it. Others are softer and worth correlating.
- Install commands issued by an agent session for a package that is not in any lockfile in your organization.
- First-time dependencies whose first published version is younger than your age threshold, or whose project has no linked source repository.
- A Python package name that exists on npm but not on PyPI, or the reverse, appearing in a requirements file.
- Outbound connections from a build or install step to hosts that are not registries or your artifact store.
- Repeated not-found errors for the same invented name across different teams, which tells you a model is converging on it.
None of these proves an attack. Legitimate new packages exist, and an engineer might add a brand-new library on purpose. They are leads for review, and they are only available if installs leave structured records with the agent, session and package name attached.
Test cases to run this month
- From a sandboxed agent session, ask for code that needs a library under a name you invented and confirmed is unregistered. Record whether the agent tries to install it, and whether anything stops it.
- From a CI runner, run pip install and npm install against a public package that is not on your approved list. The expected result is a not-found error from your proxy, not a successful download.
- Publish a harmless test package to an internal registry and try to install it before your minimum-age threshold. Confirm the install is refused and that the refusal is logged.
- Run pip install with --require-hashes from a lockfile, then add one unhashed requirement and confirm the whole install fails [5].
- Wrap an install inside a compound command, such as a test command followed by && and a package install, and confirm your approval or gateway policy still recognizes the install.
- Search your logs for one package name that failed to resolve last month. If you cannot answer which agent session asked for it, fix the logging before the next test.
The fix is verified when an invented name produces a visible question or a clean failure, and never a silent download. Test after every agent framework upgrade, since a new version can change how a terminal tool is invoked.
How Intertrace handles it
Intertrace's enforcer normalizes shell tool calls from their arguments, so a pip install or npm install inside a compound command, a substitution or a piped download is read as what it is, and normalization only ever raises the consequence tier. Agent roles decide whether installs run, ask in production or always ask, and the approval lands in one inbox with the package name and session attached. MCP servers launched through npx or uvx have their launch packages checked against OSV, including the OpenSSF malicious-packages feed. Discovery scans GitHub repositories for AI agents running in CI, so you know where unattended installs can happen; registry proxies, lockfiles and age gates remain controls you run in your own toolchain.