post: When a Dependency Check Can't Answer_□×

When a Dependency Check Can't Answer

My maintenance tool could recognize a requirements-based Python project, but it couldn’t report outdated packages or vulnerabilities for it. Returning a generic error made that limitation harder for other tools to handle.

The command is cargo upkeep python, part of my Rust maintenance tool. It reports dependency updates and security findings where the detected package manager supports those checks. Requirements-only projects used to return an error object without schema_version, so consumers expecting the normal report format couldn’t read it.

The fix gives those projects the same report structure as supported ones, with an explicit explanation of what wasn’t measured. A caller can read the result without treating a missing check as an empty list of findings.

What the package manager can tell us

The existing adapters run package-manager commands and normalize their output. For pip and pip-tools, neither provides the requirements-file report this adapter needs.

pip list --outdated reports on the installed environment. That environment may differ from the versions pinned in requirements.txt. pip-compile --upgrade resolves dependencies again and updates the requirements file, which is a different operation from reporting on its existing pins.

We also checked whether uv could read the requirements file directly. In the version checked for this change, uv audit required a project with pyproject.toml; it didn’t accept a requirements file as input. uv pip list --outdated reports installed packages, with the same mismatch as pip.

That doesn’t make requirements files impossible to audit. A dedicated scanner such as pip-audit can read them. It means this adapter doesn’t provide that check, and its report needs to say so.

Reporting what wasn’t checked

These are selected fields from the new report, with the explanatory detail strings and other fields omitted:

{
  "schema_version": 1,
  "manager": { "name": "pip_tools", "version": null },
  "complete": false,
  "capabilities": [
    { "name": "outdated", "measured": false },
    { "name": "security", "measured": false }
  ],
  "unavailable": [
    { "name": "outdated", "reason": "unsupported" },
    { "name": "security", "reason": "unsupported" }
  ],
  "outdated": null,
  "security": null
}

The reason field tells a caller what to do next:

  • not_installed: install the required tool and try again.
  • failed: the check encountered an error; inspect the failure details.
  • unsupported: this adapter cannot provide the check. Installing the detected package manager won’t add that capability.

Each unavailable capability gets its own explanation. Someone looking at the security result needs to know where they can run a vulnerability scan. Someone looking for outdated packages needs an explanation of why the installed environment isn’t being used as a substitute for the requirements file.

Those explanations serve the person reading the report. The reason codes let a script distinguish the cases without parsing sentences.

Why the command exits unsuccessfully

For pip and pip-tools projects, the command writes the full report to stdout and then exits with code 1, because neither check was performed. CI can retain the report even though the command didn’t complete the requested checks.

I want that failure to be visible. A successful dependency check with no findings means something different from a command that couldn’t check the dependencies. Returning the same exit status for both would make that distinction too easy to miss.

There is one detection limitation to keep in mind. Requirements files are a fallback after the existing project detection. This avoids mistaking a requirements export in a subdirectory for the main project, but a requirements-only subproject beneath a root pyproject.toml will still be identified as the outer project. That case remains a limitation for mixed-manager monorepos.

For a requirements-file security scan, pip-audit -r requirements.txt is a separate option. The upkeep report gives callers enough information to choose that next step instead of repeatedly trying to install a missing dependency.

If a check didn’t run, I want that in the report. Tell me what’s missing, why it’s missing, and what I can use to check it.

Sources

I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].

START
llbbl.exeprojects/posts/experiments/subscribe.dlg
© 2026v1.0.0