Developer Tools

uv 0.13 Breaking Changes: What Will Fail in Your CI

uv 0.13.0 defaults to Python 3.15, enforces --require-hashes in constraints files and switches tar backends. What breaks in CI and how to fix each one.

TechLogHub Editorial
October 20, 2026
6 min read
0 views

Share Article

Diagram of a CI pipeline where uv 0.13 checks Python pin, constraints hashes and archives before install

uv 0.13 Breaking Changes: What Will Fail in Your CI

Quick answer: uv 0.13.0, released 9 October 2026, makes Python 3.15 the default download, enforces --require-hashes inside constraints files, rejects editable requirements in constraints files, prefers native ARM64 Python on Windows ARM64, takes one path per --constraint-style flag, and switches to a stricter tar backend. Pin your Python version and your uv version in CI before you upgrade, then fix the constraint files that start failing.

Most uv upgrades are a non-event. 0.13 is not, and the failures it causes look like dependency problems rather than tool changes, which is the worst kind.

The good news is that every breaking change in the uv 0.13.0 release notes comes with a documented escape hatch or a clear fix. Here is what breaks, in the order you are likely to hit it.

1. Unpinned Python now means Python 3.15

The default stable Python is now 3.15, up from 3.14. That affects any command that downloads an interpreter when no version is requested or pinned, such as uv python install. Interpreters you already have installed and that satisfy the request are still used.

In CI, "already installed" usually means nothing, because runners start clean. A workflow that ran uv python install with no version yesterday will fetch 3.15 today. If any dependency has no wheels for 3.15 yet, you get a source build or a resolution failure that looks like the package's fault.

The fix is one line. Pin with uv python pin 3.14 or pass --python 3.14 explicitly. Pinning was always the right call; 0.13 just makes the cost of not pinning visible.

If you are moving to 3.15 on purpose, Astral's Python 3.15 post covers support across uv, Ruff and ty, and we went through the new lazy imports feature in Python 3.15 Lazy Imports: Which Mode to Use.

2. Constraints files are now taken literally

Two changes tighten how uv reads constraints files passed with -c.

--require-hashes is honoured

If a constraints file contains --require-hashes, uv now enforces it. Requirements without hashes can fail, and there is no opt-out while the directive is present. Either add hashes for everything or remove the directive.

This one bites teams that copied a hashed constraints file from somewhere else and never noticed uv was ignoring the directive. The file always said "require hashes." Now it means it.

Editable requirements are rejected

Editable requirements inside included constraints files are now an error, again with no opt-out. Move them into a -r requirements file or pass them with --editable. Constraints were never meant to install anything, so this is a correctness fix, but it will break monorepo setups that used one shared file for both purposes.

A quick way to find the first two problems before upgrading is to search your repo for constraints files containing --require-hashes or -e . If you need a pattern that handles both, our regex tester is handy for checking it against a few real lines first.

One path per flag

--constraint, --override, --exclude and --build-constraint now take a single path per flag. Repeat the flag for multiple files instead of passing a space-separated list.

Scripts that built a command like --constraint "a.txt b.txt" will now look for a single file with a space in its name. The environment variables UV_CONSTRAINT, UV_OVERRIDE, UV_EXCLUDE and UV_BUILD_CONSTRAINT still accept space-separated lists, so if your CI sets those, nothing changes.

3. Windows ARM64 gets native Python

On Windows ARM64, uv now prefers native ARM64 interpreters. If no native build is available it falls back to x86_64, then x86. That is the right default for performance, but packages with compiled extensions may not publish ARM64 Windows wheels.

To keep the old behaviour, set UV_PYTHON_ARCH=x86_64, request an explicit build such as cpython-3.14-windows-x86_64, or set python-arch: x86_64 in the setup-uv action. Astral's Python 3.15 post repeats the same default and the same UV_PYTHON_ARCH=x86_64 escape hatch.

4. A stricter tar backend

The tar-codec backend is now the default. It validates archives more strictly, so some source distributions with hard links or unsupported extensions may be rejected. Source distributions built by uv_build may also have different bytes and hashes than before.

That second point matters if you publish packages. A rebuilt sdist with a new hash will not match hashes your users pinned for a previous build. Do not re-upload an existing version; ship a new one. To opt out while you investigate, set UV_LEGACY_TAR_BACKEND=1.

The smaller changes

A few more items are worth a line in your upgrade notes:

  • No distutils patch on Python 3.10+. uv no longer installs its distutils startup patch for Python 3.10 and newer. Existing virtual environments keep it until you recreate them.
  • uv build --clear refuses dangerous output directories. It now rejects an output directory that contains a build source, including through symlinks, and checks before clearing anything.
  • Cache format changed. Performance work on HTTP caching changed the cache format, so the first run after upgrading may re-download or rebuild dependencies. Expect a slower first CI run, and do not mistake it for a regression.
  • Preview: --require-build-hashes. A new preview flag requires hashes for build dependencies too, including transitive ones. If you already run hashed installs, it closes the obvious gap.

An upgrade plan that does not ruin your afternoon

Pin first, upgrade second

Before touching uv, pin your Python version in every project and workflow. Then pin the uv version your CI installs, so the upgrade becomes a reviewed change instead of something that happens on the next run.

Upgrade in one branch and read the diff

Bump uv, run your full matrix, and expect failures in three places: constraints files, Windows ARM64 jobs, and anything that hashes sdists. If you regenerate lockfiles, review them with a diff tool rather than trusting the green tick. Our text diff checker works for quick comparisons when the CI view is noisy.

Fix files, not flags

Every breaking change has an environment variable or flag that restores old behaviour. Use them only to unblock a release, and track removing them. UV_LEGACY_TAR_BACKEND=1 in particular hides archive problems that will resurface elsewhere.

If you keep CI config in YAML and generate parts of it, converting with YAML to JSON makes it easy to script checks for unpinned python-version or uv-version keys across many workflows.

Why this release is a good sign

It is tempting to read a list of breaking changes as churn. Look at what they have in common: uv now does what your files already said. Hash requirements are enforced, constraints stop installing things, archives are validated and paths are unambiguous. That is the same direction we described in Most Package Managers Don't Verify What They Download, where the flags and lockfiles you commit are often the only integrity check you have.

If you are comparing Python tooling more broadly, the open-source directory and our developer tools hub are good places to start.

FAQ

What Python version does uv 0.13 install by default?

uv 0.13.0 installs Python 3.15 when no version is requested or pinned. Already-installed compatible interpreters are still used. Pin 3.14 with uv python pin 3.14 or pass --python 3.14 to keep the old default.

Why does uv 0.13 fail on my constraints file?

Two likely causes: the file contains --require-hashes, which uv now enforces, or it contains editable requirements, which are now rejected. Add hashes or remove the directive, and move editables to a -r file or pass them with --editable.

Can I keep passing several files to --constraint?

Not in one flag. Repeat --constraint for each file. The UV_CONSTRAINT, UV_OVERRIDE, UV_EXCLUDE and UV_BUILD_CONSTRAINT environment variables still accept space-separated lists.

How do I keep x86_64 Python on Windows ARM64?

Set UV_PYTHON_ARCH=x86_64, request an explicit build such as cpython-3.14-windows-x86_64, or set python-arch: x86_64 in setup-uv.

Why did my sdist hash change after upgrading uv?

uv 0.13.0 makes the tar-codec backend the default, and source distributions built by uv_build may have different bytes and hashes. Set UV_LEGACY_TAR_BACKEND=1 to opt out temporarily.


uv 0.13 mostly enforces what your config files already said. Pin, upgrade on a branch, and fix the files.

Stay Updated

Get the next deep dive in your inbox

Subscribe for product analysis, engineering explainers, and practical guides published on TechLogHub.

See what launched this week

One email a week: new and trending developer tools, fresh comparisons, and what shipped. Unsubscribe in one click.