ax-check rule

AXC-F004: Claimed PyPI package does not exist

An install command names a PyPI package that PyPI does not know.

ax-check is a checker being prepared for release. This page documents the rule ahead of that release; see all 50 rules.

Severityerror
KindObserved. Depends on the program and environment at run time.
Modeax-check drift
Applies toInstruction files and manifests
Pattern tagsdrift, prompt-injection
Fix in one lineCorrect the package name, or publish the package before telling agents to install it.

The file contains an install or run command for a Python package, and PyPI has no package with that name. The instructions cannot work. They are also a security risk, because anyone can register a name that is not yet taken.

What it checks

ax-check reads install and run commands from code spans and fenced code blocks. It recognises pip install, pip3 install, python -m pip install, pipx install, pipx run, uv pip install, uv add, uv tool install, uv tool run, uvx and poetry add. In JSON files it also reads command and args pairs and server.json entries whose registryType is pypi. For each package name it asks the PyPI JSON API. The rule fires when PyPI answers 404.

Why it matters

The first reason is simple: the command fails, and the agent has to guess a replacement.

The second reason is security. A PyPI name that nobody has registered is open to everyone. If your instructions name a package that does not exist, someone else can publish a package under that name. An agent that follows the instructions will run pip install or uvx on whatever now lives at that name, often without a person reading it first. The agent trusts the file, and the file now points at code written by a stranger. A typo has the same effect, as does a package you meant to publish and did not.

Check names against PyPI before you tell agents to install them.

How to fix

  • Correct the package name if it is a typo or an old name.
  • If the package is yours and unpublished, publish it before you tell anyone to install it. Publishing also stops others from taking the name.
  • If the package was removed on purpose, delete the command from the file.

Example

Find it:

ax-check drift --online SKILL.md

Before

---
name: invoice-export
description: Exports invoices to CSV. Use when the user asks for a spreadsheet of invoices.
---

# Invoice export

Install the exporter, then run it:

```bash
pip install invoicekit-exporter
invoicekit-export --month 2026-09
```

PyPI has invoicekit-export but no invoicekit-exporter.

After

---
name: invoice-export
description: Exports invoices to CSV. Use when the user asks for a spreadsheet of invoices.
---

# Invoice export

Install the exporter, then run it:

```bash
pip install invoicekit-export
invoicekit-export --month 2026-09
```

How ax-check detects it

The extraction and the rule logic are deterministic, but the result depends on what PyPI answers at the time of the run, so the same file can give different findings on another day. JSON and SARIF reports record when and where the check ran. It tokenises each command, finds the install verb, and takes the package names that follow. For pipx and uvx forms that run a tool, it takes only the first package. It reads names with extras such as name[extra] and ignores version operators when it takes the name. It asks https://pypi.org/pypi/<name>/json for each distinct name (names are compared without regard to case). Only a 404 triggers this rule. If PyPI answers with another error or cannot be reached, the package could not be checked, and the result is reported as AXC-F002 instead.

It deliberately ignores option flags (and the value after flags such as -r, -c, -e and --index-url), local paths, git+, https: and file: specifiers, and placeholders such as <package>, your-package and .... A pinned name==1.2.3 is passed on to AXC-F005.

Known limits:

  • A private index is not consulted. A package that lives only on a private index looks missing.
  • Names that differ only by case, dots, hyphens or underscores are normalised by PyPI, so they resolve to the same package. ax-check does not flag the spelling difference.
  • A look-alike name that exists but is not the intended package is not detected.

Run ax-check drift --online --dry-run SKILL.md to list each package that would be looked up, without making a request. Offline runs do not check packages. AXC-F011 reports how many were left unchecked. To silence this rule, use --disable AXC-F004.

Sources

Records in the AX evidence register that share a pattern tag with this rule. A shared tag means the record is about the same pattern, not that it tests this rule. Read the evidence class before the number.

  • EV-0015: Approvals that outlive their task raise attack success (Preprint). A preprint reports that approvals persisted beyond the context that justified them raised prompt-injection attack success by up to 35.1 percentage points on 508 AgentDojo cases, and by 24.9 points on average in live tests on three production coding agents.
  • EV-0026: Prompt injection split across tool channels evades defences (Preprint). Across 12 frontier models and over 15,000 trials (preprint), models that resisted single-channel prompt injection exfiltrated data at up to 100% when the payload was split across two channels, such as a tool description and a tool result, and seven third-party MCP security tools failed to detect it.
  • EV-0039: Launch post and documentation disagree on agent output format (Independent measurement). Cloudflare's cf launch post says JSON output is 'condensed for agents', but the cf documentation says JSON output is indented whether or not output is a terminal, and a public issue reports byte-identical output with an agent detected.
  • EV-0040: Agent skills recommended a package that does not exist (Vendor measurement). Merged pull requests in Vercel's agent plugin repository corrected skill instructions that recommended an npm package that is not published, and plugin guidance that advertised deployment cards the production MCP server does not expose.