ANTIGRAVITY LABJP
Articles/Tips & Best Practices
Tips & Best Practices/2026-05-01Intermediate

When Antigravity's AI Imports a Package That Doesn't Exist

Antigravity's AI sometimes writes imports for packages that exist nowhere on npm. Here's how to spot phantom imports in five minutes, why the model invents them, and the AGENTS.md rules I use to keep my repo clean.

antigravity432ai-agent18troubleshooting105npm4import

"pnpm install just stopped with ERR_PNPM_NO_MATCHING_VERSION." If you have ever shipped a refactor through Antigravity's agents and run face-first into that message, you are far from alone. The first time it happened to me, the import in question was react-hook-toast — a name so plausible that I doubted my own memory before I doubted the model.

The community has started calling this behavior "phantom imports" or, less charitably, "slopsquatting bait." It isn't unique to Antigravity — Cursor and Claude Code see it too — but Antigravity's manager surface tends to make the diagnosis harder, because several agents may have touched the file before you got a chance to read the diff. Left unchecked, phantom imports cost you build time and quietly widen your supply-chain attack surface. Let's walk through why this happens and how to lock it down without slowing your workflow.

Why a model invents "almost real" package names

Phantom imports are a side effect of how language models optimize for plausibility. react-hook-toast does not exist, but react-hot-toast, react-hook-form, and use-toast all do, and their tokens co-occur in training data often enough that the model treats the invented name as just as natural as the real ones. The same mechanism produces next-auth-helpers (a blend of @supabase/auth-helpers-nextjs and next-auth) and tailwind-merge-cn (tailwind-merge plus the cn utility from shadcn).

It gets worse inside Antigravity. When the manager surface dispatches a "code edit" task, it usually does not feed the entire package.json back to the worker — token budgets force a summary. If you ask for "add toast notifications" against a sparse context, the agent picks the most familiar-looking name from a fuzzy memory rather than from your actual lockfile.

In my own projects, three situations reproduce the bug almost on demand:

  • A single prompt that bundles several features (auth + notifications + analytics)
  • A bump-many-versions task where the agent edits imports as a side effect
  • A fresh workspace where only the README is in the agent's context

A fourth, more subtle case worth flagging: when a real package has been deprecated and split into two scoped successors, the agent often hallucinates a hybrid name that combines fragments of both. If your repo still references the deprecated package, you may see this pattern grow as you migrate.

A five-minute audit before you run install

Before you reach for pnpm install on agent-generated code, scan every external import against both package.json and the npm registry. The script below separates phantom imports (nowhere on npm) from ordinary missing dependencies, so you know which lever to pull. The point is not just to fail loudly — it's to label why a dependency is missing, because the fix is completely different.

# scripts/scan-phantom-imports.sh
# Usage: bash scripts/scan-phantom-imports.sh src
set -euo pipefail
TARGET="${1:-src}"
 
# 1. Pull external package names out of every import statement
mapfile -t IMPORTS < <(
  grep -rEho "from ['\"][^'\"./][^'\"]*['\"]" "$TARGET" \
    | sed -E "s/from ['\"]([^'\"]+)['\"]/\1/" \
    | awk -F/ '/^@/ {print $1"/"$2; next} {print $1}' \
    | sort -u
)
 
# 2. Cross-check against package.json AND the npm registry
for pkg in "${IMPORTS[@]}"; do
  in_pkg=$(jq -r --arg p "$pkg" '
    (.dependencies // {}) + (.devDependencies // {}) | has($p)
  ' package.json)
 
  exists=$(npm view "$pkg" name 2>/dev/null || echo "")
  if [ "$in_pkg" = "false" ] && [ -z "$exists" ]; then
    echo "🚨 PHANTOM: $pkg (not in npm registry, not in package.json)"
  elif [ "$in_pkg" = "false" ]; then
    echo "⚠️  MISSING DEP: $pkg (exists on npm, not yet installed)"
  fi
done

Expected output looks like this — PHANTOM lines are hallucinations, MISSING DEP lines are ordinary forgotten installs:

🚨 PHANTOM: react-hook-toast (not in npm registry, not in package.json)
⚠️  MISSING DEP: zod (exists on npm, not yet installed)

I keep this script in scripts/ and reference it from AGENTS.md so the agent runs it before proposing changes. Catching phantoms at the diff stage is far cheaper than after a failed install — or, worse, after a typosquat package has already landed in your lockfile. The script also runs in CI as a final guard, fed by git diff --name-only origin/main HEAD, so a phantom can never reach main even if a reviewer waves it through.

If you prefer to integrate with eslint, the rule import/no-unresolved plus eslint-plugin-import-newlines will catch phantoms statically. The shell script earns its keep when you want zero-config detection that works on any branch, including ones where eslint isn't even configured yet.

Telling Antigravity to stop guessing

Detection is half the answer. The other half is constraining the model so it stops inventing names in the first place. The rules I keep at the top of every AGENTS.md look like this. The Manager Surface gives this file higher priority than ordinary docs, so guidance here actually moves the needle.

## Dependency Discipline
- Never invent a package name. If you are not 100% sure a package exists on npm,
  call the `pnpm search` tool first and quote the registry response.
- Always update `package.json` and run `pnpm install` *in the same task*.
  Do not split "edit code" and "install deps" across separate tasks.
- If the user requests a feature that needs a new dep, list 2 candidate
  packages with weekly download counts before choosing one.

Pair that with Settings → AI Behavior → Tool Permissions, where I whitelist pnpm search and npm view for every agent. Once the agent can verify cheaply, "imagine and write" turns into "look up and write" — which is, in the end, what we want. The same approach extends to Python (pip index versions) and Go (go list -m -versions), so you can apply the same shape of rule across polyglot repos.

If you run a multi-agent setup, give the reviewer agent a separate prompt that rejects any dependency-touching PR that hasn't passed the phantom scan. This role separation is the same pattern I described in my post on agents that ignore instructions, applied here to supply-chain hygiene rather than to coding style.

What to do after a phantom slipped through

Sometimes you will only notice the problem after pnpm install succeeded — which means a real package occupied the name you trusted. Treat that as a potential typosquat or slopsquat hit and run these three checks immediately:

  • pnpm why <pkg> to see exactly where the dependency entered your tree
  • pnpm audit --prod to compare advisories before and after the change
  • Open node_modules/<pkg>/package.json, follow repository.url, and look at the GitHub stars and last commit date with your own eyes

If anything feels off — a freshly registered author, no repository link, suspicious post-install scripts — don't stop at pnpm remove. Run pnpm store prune and delete node_modules and .pnpm-store outright, then rebuild from a clean lockfile review. If you maintain an SBOM, diff cyclonedx-pnpm output in CI so unfamiliar names cannot land silently. I documented the full triage flow in the module and package error guide.

A short field guide to the names that get hallucinated

Over a year of running this scan I've kept a running list of the names that show up most often as phantoms. The patterns repeat enough that you can recognize them by sight after a while:

  • Hyphen-spliced hybridsreact-hook-toast, next-auth-helpers, tailwind-merge-cn. Two real packages welded together at a common token.
  • Plausible scoped packages@vercel/edge-cache, @nextjs/auth. The scope and short name read like an official combination, but the publisher never claimed the name.
  • Verb-noun coinagevalidate-zod, format-date-fns. Built from a real library plus a verb that fits the prompt's intent.
  • Backported "v2" namesreact-query-v5, zod-v4. The model knows a major version exists but invents a separate package instead of bumping the version range.

When a phantom looks like one of these patterns, it's almost always safe to ask the agent for the closest real package and continue. Save the more skeptical investigation for hits that don't match any of the patterns above — those are more likely to be genuine typosquats.

Wrapping up

Next time the AI hands you an import you don't recognize, don't trust it on faith — diff it against package.json and the npm registry. Save the script above as scripts/scan-phantom-imports.sh today, and add a single line to your AGENTS.md that says "run this before any code suggestion." Detection and prevention together cost less than one debugging session, and you get back the part of every workday that used to disappear into chasing imports that were never there to begin with.

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Tips2026-05-19
When Antigravity's AI rewrites your package.json dependencies and breaks the build
You asked Antigravity for a small visual change, and somehow twenty-three dependency lines in package.json got rewritten — carets stripped, pins bumped to latest, the build dead on arrival. Here is what triggers it, how to roll back cleanly, and three guardrails to keep it from recurring.
Tips2026-05-29
Why Antigravity Agent Edits Vanish with Auto Save (and How to Stop It)
When Antigravity's agent stream collides with the editor's Auto Save, parts of an applied diff silently disappear. This guide walks through the exact conditions that trigger it and a three-step fix you can keep across projects.
Tips2026-05-27
Fixing Japanese Mojibake (Garbled Text) in Antigravity's Integrated Terminal
When git log, npm errors, or filenames containing Japanese characters turn into gibberish like 譁?ュ怜喧縺 in Antigravity's integrated terminal, the fix usually takes one or two lines per platform. Here are the Windows, macOS, and WSL2 patterns I keep running into.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →