Desk Clarity

Everyday document & browser help

uv venv used one Python but uv run selects another: check the project pin

By ON Lab · Sources checked November 7, 2026

Creating an environment with uv venv --python ... does not by itself establish the Python request used by every later project command. Check the project's .python-version, its requires-python constraint, the command's explicit interpreter request, and the interpreter actually executing your program before rebuilding anything.

A project command manages project state. The fact that a shell currently activates a particular environment is not a complete explanation of that command's selection. Capture the first message about selecting or recreating an environment, especially if it differs from the version you expected.

A reader case where the pin explained the mismatch

The linked uv issue contains a report that a virtual environment created with Python 3.12 was replaced when a later uv run selected 3.13. A maintainer asked about a .python-version file. The reporter found a 3.13 pin and confirmed that removing it changed the behavior. Later discussion focused on clearer warnings.

This is a historical report from uv 0.4.30 on macOS, not proof that every current mismatch has that cause. Its useful lesson is to inspect the input that requested the interpreter instead of assuming the environment creation command permanently chose the project's version.

Four pieces of evidence to record

EvidenceQuestion it answers
requires-python in pyproject.tomlWhich versions the project declares compatible
.python-versionWhich default version is requested for this location
Explicit --python on the commandWhat this invocation asks uv to use
sys.executable and sys.versionWhich interpreter actually ran this process

Compatibility and selection are different. A lower bound such as requires-python = ">=3.9" does not mean “always use exactly 3.9.” Likewise, an executable path in an error can reveal the interpreter that was attempted without establishing that the application ran successfully.

Locate version requests before changing them

The current official documentation says uv looks for .python-version in the working directory and parents, with project or workspace boundaries and user configuration rules. A pin may therefore come from a different location than the file you were inspecting. Read the documentation for the current discovery rules instead of scanning unrelated home directories or assuming an inherited pin is absent.

uv python pin creates a version request file; it is a project configuration change, not merely a status display. Review any existing pin and team policy before replacing or deleting it. In a shared checkout, the pin may intentionally keep development and CI aligned.

uv --version
uv python list --only-installed
uv python find

These commands help inventory the tool and available interpreters. The documentation notes that uv python find can include virtual environments. Its output is useful evidence, but it does not replace reading the exact project command's output. Do not interpret a list of available downloads as proof that those interpreters are already installed.

What we actually tested offline

We used uv 0.13.7 on macOS arm64 and an installed Python 3.9.6. In a new dependency-free project, we wrote .python-version as 3.99, intentionally requesting a version that was not available. An explicit uv venv request for /usr/bin/python3 created an environment. A later project uv run without an explicit interpreter failed with exit code 2 and named the unavailable Python 3.99.

Running uv run again with the explicit /usr/bin/python3 path succeeded and printed Python 3.9.6. The successful command also reported removing and recreating .venv. We therefore do not claim that explicit selection preserves the old environment or that these commands are read-only.

No interpreter was downloaded and no package index was contacted. This experiment demonstrates the difference between an explicit environment-creation request and a later project request. It does not reproduce a switch between two installed modern Python versions, the old issue's exact version combination, an IDE kernel, or a Windows launcher.

Repeat only in a disposable directory

Save the following pyproject.toml in a new empty directory. Do not point this test at a real checkout whose environment matters.

[project]
name = "python-choice-example"
version = "0.1.0"
requires-python = ">=3.9"
dependencies = []

Save 3.99 followed by a newline in .python-version. This is an intentionally unavailable test request, not a recommended project version. Choose a real installed Python path compatible with the metadata for the explicit commands below.

uv venv --python /usr/bin/python3 --offline --no-python-downloads
uv run --offline --no-python-downloads python -V
uv run --python /usr/bin/python3 --offline --no-python-downloads python -V

The path shown is macOS-specific and was the path used in our run. Use your actual installed interpreter on other systems. Run the commands separately and preserve each exit code and complete output. If your installed interpreter is older than the requirement, the test cannot demonstrate the intended distinction.

Our first trial mistakenly passed --no-install-project to uv run. That invocation failed on an unsupported argument and did not test interpreter selection. The results described here come from the corrected commands shown above. Keeping those errors separate prevents an argument-parsing failure from being reported as a Python version problem.

Repair the intended project state

  1. Agree on the project's supported Python range and intended default version.
  2. Inspect the existing pin and any explicit interpreter flags in scripts or CI.
  3. If the pin is wrong, change it deliberately and review the diff; if the pin is right, use a compatible installed interpreter.
  4. Expect a managed project command to reconcile its environment. Preserve any useful manual setup record before proceeding.
  5. Run the actual application or tests afterward and read sys.executable from the process that matters.

Do not lower requires-python just to make an incompatible interpreter pass. That metadata describes project compatibility. Similarly, turning on automatic downloads changes resource behavior; it is not a neutral diagnostic step. The offline and no-download flags in this experiment deliberately bound that behavior.

If the terminal and editor still disagree

A terminal's successful uv run does not establish which interpreter a notebook or editor selected. Compare the executable path inside the actual failing process. Record how that process was launched and whether it is using the project's environment or a separately selected kernel. This guide does not test editor-specific configuration.

If the Python version is correct but a manually installed package disappeared, inspect exact and inexact package syncing. If the error is about a stale lockfile instead, use the lockfile freshness check. Neither is fixed merely by changing the interpreter pin without identifying the original error.

Sources checked November 7, 2026. The unavailable-version test and explicit-path comparison were actually executed. Team configuration, private environments, downloads, IDE behavior, and application compatibility remain outside that reproduction.

Sources