LLMs invent function signatures, config keys, and package names that look real. Why it happens, why hallucinated packages are a security risk, and how to catch both.
Ask an AI coding assistant to parse a config file and it might write a call to a method named loadStrict on your config object. The name fits the library's naming convention, takes the argument you would expect, and the assistant explains what it does with full confidence. It also does not exist. This is a hallucinated API: a function signature, config key, or library method invented on the spot because it fits the statistical shape of code the model has seen, not because it fits the library actually installed on your machine.
Most of the time this fails loudly and fast: an import error, an undefined method, a red squiggle before the code ever runs. That case is annoying but cheap. The two cases that matter more are the ones that do not fail fast: a config key that gets silently ignored instead of rejected, and a hallucinated package name, which turns a coding mistake into an open slot in the software supply chain the moment someone registers it. This post covers why models invent APIs in the first place, the specific security risk when the invented name is a package, and what actually catches it before it ships.
Why a Language Model Invents a Plausible API
A code-generating model produces the next most likely token given everything written so far. It is not consulting a live index of what methods actually exist on the class it is calling. Training data is full of libraries that follow predictable conventions: get and set prefixes, validate and parse verbs, a config object with attributes shaped like every other config object the model has seen thousands of times. When the real method is not well represented in training data, or the model's knowledge lags a library's current release, it fills the gap with the most statistically plausible name rather than surfacing that it does not know one. That is the same mechanism behind a hallucinated citation: fluent completion standing in for verification, because fluency is what the model was trained to produce.
Version drift makes this worse, not better. A library that ships breaking changes every few months moves faster than most models' training cutoffs, so the assistant defaults to whichever version was most common in its training data, regardless of what your lockfile actually pins. The result looks like a slightly outdated tutorial confidently presented as current fact.
Three Places Hallucination Shows Up in a Coding Session
The failure mode is the same everywhere it appears, invented specificity that sounds native to the surrounding code, but it shows up differently depending on what layer it hits:
- Function signatures: extra parameters, renamed arguments, or a return type that does not match what the real function actually gives back.
- Config keys: a YAML or JSON field, or an environment variable, that looks like it belongs to the schema but the schema silently drops or the loader errors on.
- Library methods and classes: a convenience method that "should" exist given the library's own conventions, invented because the pattern was common elsewhere in training data.
- Package names: an entire dependency invented from a plausible combination of words, the case that turns a build failure into a security question.
The Security Angle: When the Hallucinated Name Is a Package
A hallucinated function call fails at the next build. A hallucinated package name does not fail at all, at first, because nothing stops a developer or an agent from running the install command the assistant suggested. Researchers studying this at scale generated 576,000 code samples across 16 popular code-generating models and two programming languages, and found that commercial models recommended a nonexistent package in about 5.2% of outputs on average, while open-source models did so in 21.7%, producing more than 205,000 unique hallucinated package names across the study.
Sources: Spracklen et al., "We Have a Package for You!" (arXiv:2406.10279)
The attack this enables has a name: slopsquatting, coined by a Python Software Foundation security developer-in-residence and popularized soon after by security researchers tracking the pattern. It works like typosquatting but does not need a developer to make a typo. An attacker watches which hallucinated names an AI assistant tends to invent, since the same plausible-sounding name often recurs across many prompts and many users, then registers that exact name on a public package registry ahead of time. The next developer who copies the assistant's install command gets a real package with a real download count. It is just not the package they thought they were installing, and it is not a lookalike a typo-detection tool would flag, because it is not a misspelling of anything real.
Sources: Socket, "The Rise of Slopsquatting"
Context Is Part of the Fix, Not Just the Guardrail
A large share of hallucination is a grounding problem before it is a model-quality problem. The assistant does not have your actual dependency manifest, your actual installed version, or the config schema your service really uses, so it fills the gap with whatever is most common across everything it was trained on. Feeding it your real lockfile and real type definitions narrows that guessing surface directly. A persistent, private memory layer such as MemX can help here in a specific way: it keeps facts like which exact version a service pins, or which config key your team actually uses, available across sessions, so the agent stops re-guessing the same wrong signature every time a new session starts from zero.
Catching It Before It Ships
Most function, class, and method hallucinations are cheap to catch because they fail mechanically: a type checker or a build step rejects them in seconds, before a human ever has to notice. The discipline is making sure that check runs every time, not just before a pull request opens.
- Run the type checker and the build after every agent turn, not only before merge, so an invented method surfaces in seconds instead of at review time.
- Maintain a dependency allow-list the agent checks before adding any new import, so an unfamiliar package name is a flag, not a default yes.
- Diff the lockfile on every dependency change and treat any addition that was not an explicit ask as a finding to review.
- Pin exact versions on install rather than a range, so a hallucinated version string cannot silently resolve to something unintended.
Before letting an agent install a package it just suggested, run one manual check: does the registry listing have a maintainer history and a download count that predates today. A slopsquatted name is frequently a few days old with a single release and no prior activity, which a thirty-second look catches immediately.
Why an Independent Review Pass Still Matters
A type checker and a dependency allow-list catch what fails mechanically. They do not catch a config key that a lenient loader silently accepts instead of rejecting, or a package that exists, installs cleanly, is not malicious, and is simply the wrong choice for what the agent actually needed, a functional hallucination rather than a naming one. That gap is exactly what an independent reviewer with no stake in the original code is positioned to close, because its job is to check whether the dependency and its usage match intent, not whether the build passed. See security review of AI code for how an adversarial pass extends past this specific failure mode, and common mistakes teams make with AI coding tools for how hallucinated APIs fit into the wider pattern of unread diffs and skipped checks.
| Check | Catches | Misses |
|---|---|---|
| Type checker + build | Wrong signatures, missing methods, broken imports | Config keys silently accepted, packages that exist but are the wrong fit |
| Dependency allow-list | Any package not already approved, including hallucinated names | A malicious update published under a name already on the allow-list |
| Lockfile diff review | Unexpected additions the agent introduced without being asked | A dependency the agent was explicitly told to add but should not have been |
| Independent review pass | Intent mismatches, wrong-but-valid packages, unsafe defaults | Little; it runs last specifically to cover what the earlier checks miss |
A hallucinated function call fails loudly, in seconds, at build time. A hallucinated package name fails quietly, sometimes months later, once someone else has already registered it.
Frequently asked questions
01Does a stricter type checker eliminate hallucinated APIs?
It eliminates most of the mechanical ones: calls to methods and functions that do not exist fail at compile or import time. It does not catch a config key a loader accepts silently instead of rejecting, or a package that installs cleanly but is not what the agent actually needed. Those need a review step, not just a stricter compiler flag.
02Is slopsquatting an active attack today or a theoretical risk?
It has moved past theoretical. Security researchers have registered hallucinated package names as a proof of concept and observed the pattern recur across models and prompts, and registries have removed flagged packages after the behavior was documented publicly. Treat any package name an assistant suggests as unverified until it has been checked, the same way you would treat an unfamiliar link.
03How is a hallucinated package different from a normal broken dependency?
A normal broken dependency, a typo in a real package name, fails immediately because nothing by that exact spelling exists yet either. A hallucinated package name is different because it is not wrong the moment it is generated. It becomes dangerous later, specifically if an attacker notices the same name recurring and registers it before you install it.
04Does TLM Forge specifically detect hallucinated packages?
TLM Forge does not run a hallucination-specific scanner. It puts the general defenses into the same loop every change goes through: build and type checks enforced as part of mechanical TDD, and an independent review pass before anything ships. A new dependency that was not part of the spec gets flagged the same way any other unreviewed scope creep does, in how it works, rather than relying on a developer to remember to check it by hand.