Released on 2026-07-28.
Since we released uv 0.11.0 in March, we've accumulated changes that improve correctness, safety, and compatibility with specifications, but could break some workflows. This release contains those changes; many have been marked as breaking out of an abundance of caution.
We expect most users to be able to upgrade without making changes.
There are no breaking changes to the configuration of the uv build backend. If your [build-system] table includes an upper bound on uv_build , update it to allow uv_build 0.12, e.g., uv_build>=0.11.32,<0.13 .
Define build systems by default with uv init (#19197) Projects created with uv init now declare a build system and are packaged by default. This was the default project layout all the way back in v0.3, but we found that the use of the hatchling build system was confusing to newcomers and consequently dropped use of a build system by default in v0.4. Since then, we've created our own build system ( uv_build ) with tight integration with uv and are excited to restore the default to a best-practice project layout. Previously, uv init example created an unpackaged layout containing main.py and a pyproject.toml without a build system. The project could declare dependencies but was not itself installed into its virtual environment. Now, uv init example defines a [build-system] using uv_build , places application source code in src/example , and includes a [project.scripts] entry named example . Defining a build system allows the project to be imported from tests or other code, installed as a dependency, and run as a command: $ uv init example $ cd example $ uv run example Hello from example! Existing projects are unaffected. Use uv init --no-package example to create the previous unpackaged layout without a build system. See the project creation documentation for more details. This stabilizes the packaged-init preview feature.
Reject unsupported source distribution and wheel archive formats (#18927) PEP 625 requires source distributions to use .tar.gz archives. Previously, uv also accepted legacy formats such as .tar.bz2 and .tar.xz . Those formats are now rejected, including when referenced by an existing lockfile. Legacy .zip source distributions remain supported for backwards compatibility. Wheels and other ZIP archives can no longer contain entries compressed with bzip2, LZMA, or XZ. Entries must use the stored, DEFLATE, or zstd compression methods. Removing support for uncommon compression methods reduces uv's compression dependencies and the attack surface exposed when processing untrusted packages. You cannot opt out of this behavior. If you depend on a legacy source distribution that uses an unsupported format, we recommend rebuilding it as a .tar.gz archive and regenerating any lockfile containing references to the legacy archive.
Reject wheel files that could replace the Python interpreter (#20748, #20749) uv already rejected wheel entry points named python , but case variants such as Python were still accepted. On case-insensitive filesystems, including common macOS and Windows setups, these entry points could overwrite the virtual environment's interpreter. Wheels could also place interpreter files in their .data/scripts directory or in paths such as .data/data/bin/python , bypassing the entry-point check and replacing the interpreter during installation. uv now rejects case-insensitive variants of reserved interpreter names and wheel data files that would be installed over an interpreter. This includes names such as Python , python.py , and Python.exe , along with other reserved interpreter names and their versioned variants. You cannot opt out of these checks. Rename conflicting entry points or wheel data files and rebuild the affected wheel.
Prefer stable releases before falling back to pre-releases (#19993) A dependency can introduce a pre-release requirement after resolution starts. uv previously required each package's pre-release eligibility to be known before resolution began: the default if-necessary-or-explicit mode allowed them for direct requirements that explicitly requested a pre-release, or for packages that only published pre-releases. This meant that a pre-release requirement discovered in a dependency's metadata, e.g., example>=2.0.0b1 , would fail to resolve even when a compatible pre-release existed. To resolve it, you had to add that dependency as a direct requirement or allow pre-releases across your entire dependency graph. The default mode is now if-necessary . uv tries stable candidates first and falls back to pre-releases when no stable candidate satisfies the active constraints. Like pip, uv now supports pre-release requirements discovered transitively, but can select different versions than previous uv releases when both stable and pre-release candidates are available. You can opt out of automatic pre-release selection with --prerelease disallow . Alternatively, --prerelease allow considers pre-releases without first preferring stable releases, and --prerelease explicit only allows them for direct requirements that mention a pre-release. The old if-necessary-or-explicit mode distinguished between explicitly requested pre-releases and packages with no stable releases. That distinction is unnecessary now that if-necessary handles both cases, including transitive requirements. The old name remains available as an alias but is deprecated and will be removed in a future release.
Respect --require-hashes directives in requirements.txt (#19336) Previously, uv pip install and uv pip sync warned about --require-hashes inside a requirements.txt file but still installed dependencies without checking their hashes. Now, the directive enables hash-checking mode, just as if --require-hashes had been passed on the command line. For example, this requirements file is no longer accepted because the requirement is neither pinned nor hashed: --require-hashes anyio You cannot opt out while the directive is present. Pin every requirement with == and provide its hash, or remove --require-hashes if hash checking is not intended.
Reject MD5-only hashes in hash-checking mode (#20758) Previously, uv pip install --require-hashes and uv pip sync --require-hashes accepted requirements whose only available digest used MD5. MD5 is not collision-resistant, so relying on it undermined installations that require hash verification and differed from pip's behavior. Hash-checking mode now requires at least one secure digest for every requirement. For example, the following requirement is rejected unless a secure hash, such as SHA-256, is also supplied: anyio==4.0.0 --hash=md5:420d85e19168705cdf0223621b18831a A secure hash can be supplied directly on the requirement or in a matching constraints file. Ordinary hash verification without --require-hashes continues to support MD5. You cannot opt out while hash checking is required. Regenerate affected hashes with SHA-256 or another supported secure hash.
... continue reading