Skip to content

What is a Dependency Cooldown?

A dependency cooldown tells a package manager to ignore any release published within the last few days. Instead of resolving to the newest version the instant it appears, the resolver waits until each candidate has existed in public for a set period.

The point is timing. When an attacker publishes a compromised version of a popular package, security researchers and registry teams usually find it fast. A cooldown makes sure you are not the one who installs it in the gap before they do.

Why delay an upgrade you could take now?

Most malicious uploads to PyPI are caught and removed within hours. The Axios compromise was discovered about three hours after release; the s1ngularity packages were live for roughly four. In both cases, a resolver that refused to install anything newer than a few days old would never have seen the malicious version.

That is the whole trade a cooldown makes. You give up immediate access to brand-new releases, and in return you let the community vet each version before it can enter your dependency tree. For a supply-chain attack that lives for hours, a window measured in days is decisive.

Fixed date or rolling window?

A cooldown is one of two things a time filter can express, and the difference matters.

A fixed date pins resolution to a point in the past. “Resolve as if it were June 15, 2023” produces the same dependency graph every time, forever. That is the tool for reproducing an old environment or bisecting when a dependency started failing, and it is covered in How to Use --exclude-newer for Reproducible Python Environments.

A rolling window is anchored to the present instead. “Ignore anything published in the last seven days” trails the current date, so the cutoff advances as time passes. This is the cooldown: a moving buffer that always keeps the newest releases out until they age past the threshold.

uv expresses both through the same exclude-newer setting on uv. A timestamp gives the fixed date; a duration such as "7 days" gives the rolling window.

Why the window stops moving once you lock

A rolling window sounds like it should slide forward on every command, but it does not, and this is the detail most descriptions skip.

When uv resolves with a duration, it converts that duration to a concrete timestamp relative to the current moment, then records the timestamp in uv.lock. Subsequent uv sync runs read the frozen timestamp rather than recomputing “seven days ago.” The window only moves when a fresh resolution runs, such as uv lock --upgrade or uv add.

The behavior is deliberate. A cooldown protects the moment new versions enter the tree, which is resolution. Once resolution has happened, the lockfile already pins the exact versions, so re-filtering on every install would add nothing but nondeterminism. The cooldown and the lockfile cover different moments, and they work together.

How long should the window be?

Seven days is the common recommendation. It catches the large majority of malicious uploads, which are usually pulled within hours, while asking most projects to wait only a week for new dependency versions. Datadog’s analysis settles on one week as standard guidance, with twelve hours as an aggressive lower bound.

Thirty days suits production deployments and internal tooling, where stability is worth more than fast access to new releases. The cost of a longer window is missing legitimate patches for longer, which is why uv also lets a single package opt out through exclude-newer-package when an urgent fix lands inside the window.

How package managers converged on cooldowns

The idea is not specific to uv or even to Python. Several tools converged on it during the 2026 wave of registry attacks:

  • uv reads a duration from exclude-newer. See How to Protect Against Python Supply Chain Attacks with uv for the configuration.
  • pip added --uploaded-prior-to in 26.0 for a datetime cutoff and extended it to accept durations in 26.1.
  • Dependabot began waiting three days by default before opening a version-update pull request in July 2026, while still letting security patches through immediately.
  • Renovate offers minimumReleaseAge, and npm, pnpm, and Yarn each shipped a minimum-age gate in 2026.

Every one of these refuses a release until it has survived in public for a set period. The vocabulary differs; the mechanism does not.

When a cooldown does nothing

A cooldown depends on knowing when each version was published, which comes from the upload-time field defined by PEP 700 index metadata. PyPI publishes this field. Custom or private indexes may not, and where the field is missing, exclude-newer has no effect for packages served from that index.

A cooldown also does not identify malware; it only buys time for someone else to. It pairs naturally with an install-time check against known-malicious advisories, which acts on the detections a cooldown is waiting for. Treat the cooldown as one layer, not the whole defense.

Learn More

Last updated on