> ## Content Index
> Fetch the complete content index at: https://debugly.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# The Package the Model Invented That Someone Else Registered
- URL: https://debugly.dev/ai-hallucinated-package-slopsquatting/
- Published: 2026-10-09T14:07:00.000Z
- Updated: 2026-10-17T05:56:22.000Z
- Description: The model suggested a dependency that did not exist, the install command ran anyway, and the name resolved because a stranger had registered it.
- Author: Rohit Bhadani
- Tags: Error Autopsy, AI, Security

The build broke with an import error, which was ordinary, and the model suggested a fix, which was also ordinary, and the fix named a package, and the package name was plausible, and the install succeeded, which is where the incident actually began, because the install succeeding was the signal everyone trusted, and the name was not in the index before, it was in the index because someone had put it there, and the someone was not us, and the package was not the package the model meant, because the model did not mean a package, the model meant a string that looked like one.

This is the anatomy of the hallucinated dependency, and of the specific property that turns it from a typo into a supply chain incident: the install step treats a resolvable name as an existing package, and the resolution is a race the attacker can win, because the attacker can register any name the model is likely to invent.

This was Python, but the mechanics are the same in npm, and the mechanism is current, and general.

## The hallucination is not the bug

A model generating a plausible package name is expected behaviour, because the model generates the most likely next token, and the most likely next token after "import" is a name that looks like a real package, and the name looks real because the model learned the shape of real names, and the shape is what it reproduces, not the registry entry. The hallucination is the model doing its job, which is producing plausible text, and the plausibility is the point, and the point is the trap.

The bug is downstream, at the install, which converts the plausible string into a real dependency, and the conversion is silent, because the installer's contract is "resolve this name or fail", and the name resolved, so the installer reported success, and the success was the trust signal that carried the hallucination into the dependency tree, and the tree into the image, and the image into production.

The hallucination produced a string. The install produced a package. Only the second one is the incident.

## Why the name resolved

The registry is a namespace with no reservation for names that models are likely to invent, and the models invent names that follow the conventions of real packages, because the conventions are what the model learned, so the invented names cluster in the same region of namespace as the plausible ones, and an attacker who reads the same conventions can register the names in advance, and the registration is cheap, and the wait is passive, and the moment a developer runs the install on the hallucinated name, the attacker's package is what arrives.

This is the same shape as typosquatting, where the attacker registers a near miss of a real name and waits for a keystroke, and the difference is the source of the typo: in typosquatting the human mistypes, and in this case the model mistypes at a much higher rate and with much better plausibility, so the volume of candidate names is larger and the developer's suspicion is lower, because the name came from a tool rather than from their own fingers, and the tool feels authoritative.

The install succeeding is the wrong signal, because the installer is answering "does this name exist now", and the question everyone meant was "does this package exist and is it the one we want", and the two questions diverge exactly when the name was invented, which is the gap the attacker occupies.

## Why the review did not catch it

The review saw a new dependency, and a new dependency is a normal thing to add, and the name looked correct, and the model had explained why it was needed, and the explanation was fluent, and the fluency is the reason the review passed, because the review's job is to check whether the change makes sense, and the change made sense, and the sense was generated, and the generated sense is indistinguishable from the real thing at the speed a review runs.

This is the trust misplacement from [reviewing code the author cannot explain](https://debugly.dev/reviewing-code-the-author-cant-explain/), and the shared property is the one worth fixing.

## What actually fixed it

**Verify the dependency against the registry, not the name.** The check is not "does this name look right", it is "does this package exist, when was it published, who maintains it, how many downloads, is the version consistent with the model's claim", and the check is one click, and the click is the review's actual work, and the work is what the fluent explanation replaced.

**Pin and hash every dependency.** The lockfile with hashes means the installed artefact is the one that was reviewed, and the hash is the identity, and the identity is what the name cannot provide, because the name is attacker controllable and the hash is not, and the lockfile is therefore the control that converts a plausible name into a verified artefact.

**Fail the build on new dependencies.** A new dependency is a change of trust boundary, and the boundary change should require an explicit approval, and the approval is the gate the hallucination cannot pass, because the gate asks for provenance and the hallucination has none, and the gate is the cheapest control in the list.

**Treat model-suggested dependencies as untrusted input.** The model's output is text, and text that names a package is a claim about the registry, and the claim should be verified by the same mechanism that verifies any claim, which is checking the source, and the source is the registry, and the check is the fix, and the discipline is the same as the one in [the secret scanner that ran after the push](https://debugly.dev/secret-scanner-after-push/), and the common root is the property, not the platform.

## The rule I keep

A model can generate a plausible package name that does not exist, and the registry will resolve it if an attacker has registered it, and the install will report success, and the success will be trusted. Verify dependencies against the registry rather than the name, pin and hash the artefacts, gate new dependencies on approval, and treat model-suggested dependencies as untrusted claims.

The build broke, and the model fixed it, and the fix installed, and the install worked, and every step reported success, and the success was the attack surface. I now check for that property first, because it is where the failure actually lives.

---

Published on [debugly.dev](https://debugly.dev/) by [Rohit Bhadani](https://debugly.dev/about/) | Founder of [Krova Cloud](https://krova.cloud/?ref=debugly.dev)