Python Dependency Hallucination: Why AI Coders Invent Libraries That Don't Exist

USENIX Security 2025: 19.7% of AI-recommended packages don't exist. Lasso Security's PoC received 30,000+ downloads. How the PyPI attack surface works, and how BrassCoders's phantom-package scanner catches it before pip install runs.

Copper Sun Brass Team · · 7 min read
securitydependencieshallucinationsupply-chain

Somewhere in the pull request your team merged last Thursday, there’s an import statement for a Python package that doesn’t exist. The AI coding assistant that wrote it had no idea. It returned the import with the same confidence it uses for requests and pandas. And if an attacker has already registered that name on PyPI — which takes about ten minutes — you ran their code the next time you did pip install.

This is the mechanics of slopsquatting: the class of supply-chain attacks built on AI-generated package names that nobody published yet. It isn’t hypothetical anymore. The research is in, the proof-of-concept attack ran, and the download counts from that proof-of-concept are the most clarifying number in this entire conversation.

The Hallucination Mechanic

USENIX Security 2025 measured AI-recommended package hallucination rates across major coding models and found that 19.7% of AI-recommended packages do not exist on the relevant registry — roughly one in five across all models in the study.

That number deserves a moment. Most developers assume hallucination is an edge case: the AI gets confused on unusual prompts, produces something weird, and the reviewer catches it. At 19.7%, hallucinated packages aren’t edge cases. They’re a predictable output of using AI-assisted coding without a verification layer.

The mechanism is well-understood. LLMs generate package names by pattern-matching against the imports they saw during training. A model trained on hundreds of millions of Python files has seen huggingface-transformers, huggingface-hub, huggingface-utils, and a dozen similar variations. When it generates code that needs a Hugging Face utility, it produces whichever variation fits the surrounding syntactic context — not whichever variation actually exists on PyPI. The model has no grounding in the registry. It’s token prediction, not package lookup.

The model doesn’t know it’s guessing. It returns import huggingface-utils with the same tone it uses for import os. No warning. No hedge. The syntactic structure is valid, so the linter passes. The mistake only surfaces when the package isn’t found — or when it is found, by someone who put it there on purpose.

How PyPI Makes It Worse

PyPI allows anyone to register any available package name for free, instantly, with no identity verification and no review of contents. BrassCoders’s phantom-package scanner checks PyPI’s JSON API for this reason: a 404 on a package name is a meaningful signal, because the alternative to 404 is a package that exists and will run on pip install.

Most developers know typosquatting: registering reqeusts to catch developers who mistype requests. PyPI has fought typosquatting for years with mixed results, because the open-registration model is structurally favorable to attackers. A new variant can be up in under ten minutes, and PyPI’s review capacity can’t keep pace with registration volume.

Slopsquatting is a structural upgrade to the same attack. Instead of guessing what typos developers make, the attacker watches what names AI assistants produce. The source of target names shifts from human error to model output — and model output is more consistent than typos. If huggingface-cli is the name a specific model reliably generates for a specific task, every developer using that model on that task is a potential victim.

The attack chain is short. The attacker needs a list of hallucinated package names (observable from public AI-generated code on GitHub or from direct model testing), the ability to run pip, and ten minutes. The packages go up faster than any registry can review them.

The Lasso Security Proof-of-Concept

Lasso Security, a 2024 study on AI-generated package hallucinations, didn’t just document the risk theoretically. Their researchers registered a hallucinated package name — huggingface-cli — that Claude, GPT-4, and Gemini all recommended in response to realistic coding prompts. The payload was benign; they published the package as a proof-of-concept and logged install counts.

That package received over 30,000 downloads from real developer machines before Lasso took it down.

The number matters because it collapses the distance between “interesting research finding” and “actual attacker surface.” The Lasso researchers never targeted anyone. They registered one name and waited. The AI assistants sent users to them. 30,000 times.

A real attacker registering a malicious variant of that same package would have had 30,000 opportunities to run arbitrary code at install time. Python’s setup.py and the postinstall hook pattern both execute arbitrary code during install — by design, with no sandboxing. The install-time code execution model was built for legitimate build steps. Slopsquatting turns it into a delivery mechanism.

The Lasso proof-of-concept is the clearest data point on what the USENIX 19.7% hallucination rate means in practice. The rate is the frequency of the opportunity. The Lasso PoC is a measurement of what happens when someone takes it.

What BrassCoders’s Phantom-Package Scanner Does

BrassCoders’s phantom-package scanner parses every Python import statement in the scanned codebase, resolves the package name, and issues a GET request to the PyPI JSON API at pypi.org/pypi/<name>/json. Any package that returns 404 is flagged as a phantom-package finding. The check runs automatically as part of brasscoders scan . — no extra configuration needed.

The scanner also accepts an explicit flag: brasscoders --check-package-hallucination scan . triggers the same check. Either path produces the same findings in the YAML output.

The check runs before pip install. That’s the entire point. By the time your CI pipeline runs pip install -r requirements.txt, you’ve already decided to trust every package on that list. The phantom-package scan happens at code-review time — when the import statement is still just a string in a .py file, before any package fetches run.

What gets sent over the wire: the bare package name. No source code, no project name, no surrounding context. PyPI’s JSON API is a public read endpoint; querying it is no different from running pip show on an uninstalled package.

One honest limitation: the scanner checks what your code imports, not what’s in your requirements.txt or pyproject.toml. If a package is listed in your manifest but never imported in a .py file BrassCoders scans, it won’t be checked. For full dependency-tree coverage — including CVEs in packages you have installed but don’t import directly — pair BrassCoders with Snyk or pip-audit. They cover different windows. BrassCoders catches hallucinated names before install; the others catch real packages with known vulnerabilities after install.

Detecting It Without BrassCoders

There are other approaches, and you should know what they cover and where they stop.

pip-audit, the tool maintained by the Python Packaging Authority, checks installed packages against the Open Source Vulnerabilities database and PyPI’s advisory feed. It’s a strong CVE scanner for packages you’ve already installed. It doesn’t catch hallucinated names before install, because hallucinated packages aren’t in your environment yet — there’s nothing to audit.

Socket (socket.dev) monitors npm packages for supply-chain indicators. Their Python coverage is narrower than their npm coverage. The tool is oriented toward installed-dependency analysis, not pre-install hallucination detection.

The manual approach works: iterate over your imports, query pypi.org/pypi/<name>/json for each one, flag the 404s. A shell script or a short Python snippet can do this. The gap is automation. You’d need to wire this into your PR process and re-run it every time the codebase changes. BrassCoders does that wiring.

The key window — between AI generating the import and pip install running — is where the defense lands. Any tool that operates after install is operating after the attacker has already had their opportunity.


The USENIX 2025 number and the Lasso 2024 proof-of-concept together make the same argument from different angles. At 19.7%, the hallucinations are frequent enough to reach every team using AI coding assistance. At 30,000 downloads from a single benign PoC package, the attack surface is large enough to make slopsquatting economically attractive for any attacker who runs the math.

The phantom-package scanner in BrassCoders is a pre-install gate. Run it before you trust the imports.

pip install brasscoders
brasscoders scan .

Full install instructions at /install/.

Frequently Asked Questions

How common is AI package hallucination in Python?

USENIX Security 2025 measured hallucinated-package rates across major AI coding models and found that 19.7% of AI-recommended packages do not exist on the relevant registry. The rate varies by model, language, and prompt type — but 1-in-5 is the published baseline across all models in the study.

What is slopsquatting?

Slopsquatting is a supply-chain attack where an attacker registers package names that AI coding assistants frequently hallucinate. When a developer copies AI-generated code and runs pip install, the malicious package is fetched and its install-time hooks execute. Lasso Security's 2024 proof-of-concept registered one such hallucinated name and received 30,000+ downloads from real developer machines before taking the package down.

Why does PyPI make this attack easier than other registries?

PyPI allows anyone to register any available package name for free, with no identity verification and no content review. A package is live within seconds of registration. This open-registration model is what made typosquatting effective for years, and it's the same property that makes slopsquatting work — the attacker's only cost is knowing which names to register.

How does BrassCoders's phantom-package scanner work?

BrassCoders parses every import statement in a scanned Python codebase, resolves the package name, and issues a GET request to the PyPI JSON API. Any package that returns 404 is flagged as a phantom-package finding. The check runs automatically as part of brasscoders scan — no extra flags needed, though --check-package-hallucination also triggers it explicitly.

Does BrassCoders's scanner replace pip-audit or Snyk?

No — they cover different windows. BrassCoders's phantom-package scanner catches hallucinated names before pip install runs, before the package is even in your environment. pip-audit and Snyk scan packages that are already installed, checking for known CVEs. The right setup pairs both: BrassCoders catches names that don't exist, Snyk or pip-audit catches real packages with known vulnerabilities.

Can I use brasscoders scan on a codebase with private packages?

Yes. If your code imports internal packages not published on PyPI, the phantom-package scanner will flag them as unresolvable. Pass the --ignore-packages flag with your internal package names to skip those. The rest of the import statements still get checked against PyPI.