PyPI now rejects new files on releases older than 14 days
PyPI closed the window where a stolen publishing token can poison a release you already shipped. Here's what breaks in your CI and how to fix it.
PyPI just made an old release immutable in the way that actually matters. As of the patch merged July 8, 2026, PyPI rejects new file uploads to any release older than 14 days. If your build publishes a late wheel — a macOS arm64 build that lands a month behind, a manylinux rebuild, a new Python version added to the matrix — that upload now fails. It's a small break in exchange for closing a real attack path, and the fix is one version bump.
What actually happened
Seth Larson and Mike Fiedler announced the change on the PyPI blog on July 22, 2026. The idea has been floating since PEP 740 discussions in January 2024, and got restarted in March 2026 after the LiteLLM and Telnyx package compromises.
The threat model is specific. Steal a project's publishing token or hijack its release workflow, and before this change you could quietly add a file to a version that shipped two years ago — a version thousands of requirements.txt files pin exactly and nobody re-reviews. The poisoned artifact inherits all the trust the original release earned. Worse, it leaves the release in a half-state where some files are compromised and some aren't, which is miserable to clean up.
PyPI checked the blast radius before shipping. Across the top 15,000 projects, only 56 had published a 3.14-compatible wheel more than 14 days after that release was available. Consensus at the PyCon US 2026 Packaging Summit was that requiring a version bump for new Python support is a fair trade.
One caveat worth reading twice: there are no defined semantics yet for "release no longer accepting files." Don't build tooling that depends on this behavior until the Upload 2.0 API standardizes it.
Why this PyPI change matters for your release pipeline
If your CI publishes wheels in more than one job, check whether any of them can finish more than 14 days after the first. Split builds, self-hosted runners for exotic targets, and manual "we'll add the Windows wheel later" workflows are the ones that break. The answer isn't a workaround — it's cutting 1.4.1 instead of backfilling 1.4.0.
Then do the thing this change is actually pointing at. If a stolen token is your threat model, stop having long-lived tokens. Use PyPI's Trusted Publishing so your GitHub Actions workflow authenticates with a short-lived OIDC token and there's no secret to steal. On the consuming side, pin by hash, not just version — pip install --require-hashes turns "this package changed" from an incident into a failed build.
PyPI shrank the window. Your token hygiene decides whether the window was ever the problem.
Key takeaways
- PyPI now rejects new files uploaded to releases older than 14 days (merged July 8, announced July 22, 2026)
- Audit any CI job that can publish a wheel weeks after the initial release — cut a new patch version instead of backfilling
- Move off long-lived API tokens to Trusted Publishing (OIDC) so there's no credential sitting in a secrets store
- Pin dependencies by hash with
--require-hashesso a swapped artifact fails your build instead of shipping
Supply-chain hardening is cheap when it's designed in and expensive when it's retrofitted after an incident. We build release pipelines with short-lived credentials, hash-pinned dependencies, and reproducible builds — so a compromised token is an annoyance, not a breach. See how we work.
Sources: The Python Package Index Blog, Help Net Security.
- #pypi
- #python
- #supply-chain
- #ci-cd
- #security
Tommy Rush — Founder, Rush Commerce
Operator turned builder. 15+ years running operations — now shipping the systems businesses run on. More
Get The Rush Report weekly — one email, zero fluff.
Keep reading
Cursor's ₹649 India tier: dev seats go geographic
Cursor launched a ₹649/month India-only plan, roughly a third of its $20 Pro tier. AI dev tool pricing is now segmented by market, and that changes your budget math.
Read itvBulletin RCE exploit went public: get to 6.2.2 today
A working exploit for vBulletin CVE-2026-61511 dropped four weeks after the patch. Unauthenticated remote code execution, no in-the-wild reports yet. That window closes fast.
Read it