Skip to content

Dependabot declines transitive security updates in uv.lock

This is a finding

Something I found or learned, or a discovery that cost time once: cheap by design, and true of a moment rather than in general.

Versions: Dependabot as hosted in September 2026, against a uv project. The uv updater is young and moving; re-check before relying on any of this.

The symptom

Security alerts against packages in uv.lock stay open for months and no security pull request is ever raised, whilst ordinary version-update pull requests arrive and merge normally. vulnerability-alerts and automated-security-fixes are both enabled, so the configuration looks correct.

The security-update jobs do run, and they fail:

| security_update_not_possible | {                                            |
|                              |   "dependency-name": "cryptography",         |
|                              |   "latest-resolvable-version": "50.0.0",     |
|                              |   "lowest-non-vulnerable-version": "50.0.0", |
|                              |   "conflicting-dependencies": []             |

That error reads as an unsatisfiable version constraint, and it is not one. The two versions agree: Dependabot found a resolvable, non-vulnerable version and then declined to write it. conflicting-dependencies is empty because nothing conflicts.

What it turned out to be

The affected packages were transitive: present in uv.lock, absent from pyproject.toml. Two settings, each reasonable-looking, kept them out of reach.

allow defaults to direct dependencies. With no allow block, the job definition in the run log reads "allowed-updates":[{"dependency-type":"direct","update-type":"all"}] and "update-subdependencies":false. The run then logged exactly ten Checking if ... needs updating lines against a fifty-package Dependency Snapshot: in the same log. The bot had never looked at the vulnerable packages at all.

versioning-strategy: lockfile-only makes it worse, not better. It reads as "only write the lockfile", which is precisely the intent for a uv project. For the uv updater it also stops transitive dependencies being resolved (dependabot-core140731), so it forbids the only edit that could have fixed them.

Two things it was not, both of which cost time as hypotheses. cooldown does not apply to security updates, so it cannot be the cause; a cooldown configured in the package manager rather than the bot is a different matter and does block them. And the "Check for updates" button in the dependency graph re-runs version updates only, so a null result from it says nothing about the security-update path.

How to get past it

Ask for the whole lockfile, and delete the strategy:

- package-ecosystem: uv
  directories: ["/"]
  allow:
  - dependency-type: all     # without this, direct dependencies only
  schedule: {interval: weekly}

Where the manifest's own requirements are unpinned or range-pinned, dropping versioning-strategy does not invite Dependabot to rewrite pyproject.toml: there is nothing to widen, and it writes uv.lock alone as before.

This also routes around the security-update path rather than waiting for it. The weekly version update now proposes the transitive bumps, which clears the alerts whether or not the security job ever works.

To unblock an alert immediately, without the bot:

uv lock --upgrade-package cryptography

Takeaway

Read the job definition at the top of the run log, not the error at the bottom. The error described a version conflict that did not exist; the job definition stated the actual cause in two fields. The general form is Keep transitive dependencies in the regular update cycle.

The following pages link to this page: