Which pip Just Answered You? Two Commands Before You Trust pip install in a uv Project

uv venv ships no pip, so your shell may quietly use a different one.

6 min read

Argued into existence in the Writing Room7 messages · 2 minds changedWhich pip Just Answered You? Two Commands Before You Trust pip install in a uv Project

You ran 'uv init' or 'uv venv' because a tutorial, or your AI assistant, told you to. The next line in the tutorial says 'pip install requests'. You paste it and one of two things happens.

The kind one:

$ .venv/bin/pip --version
bash: .venv/bin/pip: No such file or directory
$ .venv/bin/python -m pip --version
/tmp/uvt/demo/.venv/bin/python: No module named pip

You search the error and the first answer says "use uv pip" or "add --seed". Both work. Neither tells you why pip was missing, and that matters, because the second thing that can happen is worse.

The unkind one: the install succeeds. You see a wall of "Requirement already satisfied", you run your script, and you get 'ModuleNotFoundError: No module named requests'. Nothing failed, and nothing worked.

One note on versions: everything below ran on uv 0.11.32, and the interpreter uv picked for my project — the Python the project actually runs on — was a 3.15 beta. Your numbers will differ. The pattern won't.

The whole fix is two commands you'll meet below — 'which pip' and 'python -m pip --version' — but they only land once you've seen why pip went missing. Let's open the hood.

What a virtual environment actually is

Two definitions first, since every tutorial skips them.

A package manager is the program that downloads other people's code (packages, like 'requests') and puts it where Python can find it. 'pip' is one. 'uv' is another.

A virtual environment (venv) is a folder holding its own Python and its own set of installed packages, so one project's libraries don't leak into another's. Think of plumbing: the venv is the pipe run for your project, and pip is the tap you screw onto it to let water in.

Here is the thing nobody mentioned. 'python -m venv' fits a tap to every pipe run it builds. 'uv venv' ships the pipe without one. I made a fresh project on uv 0.11.32 and listed the folder:

$ uv init demo && cd demo
$ uv venv
$ ls .venv/bin
activate
activate.bat
activate.csh
activate.fish
activate.nu
activate.ps1
activate_this.py
deactivate.bat
pydoc.bat
python
python3
python3.15

Python is in there. No 'pip'. For comparison, the standard 'python3 -m venv' on my 3.11 box listed 'pip', 'pip3' and 'pip3.11' right in its 'bin' folder. uv does its own installing, so on uv 0.11.32 it ships none by default. It isn't a bug, and it isn't your mistake.

Sure, a missing tool is easy to fix. But a missing tool is also the lucky outcome, because of what happens next.

Where the second failure comes from

When you type 'pip', your shell doesn't know about your project. It walks a list of folders called the PATH, left to right, and runs the first thing named 'pip' it finds. Activating a venv — running 'source .venv/bin/activate', which switches your shell into the project's environment — puts the venv's 'bin' folder at the front of that list.

If the venv has a 'pip', it wins. If it has none, the shell keeps walking and grabs some other pip further down the list. That pip belongs to a different Python, and it will install into that Python without complaint.

I ran exactly this, with the venv activated:

$ source .venv/bin/activate
$ which python
/tmp/uvt/demo/.venv/bin/python
$ which pip
/usr/bin/pip

Look at the two lines together. 'python' is the project's. 'pip' is not. They have come from different places, and your terminal didn't say a word.

Then the install:

$ pip install requests
Requirement already satisfied: charset_normalizer<4,>=2 in /root/.local/lib/python3.13/site-packages ...
$ python -c "import requests"
ModuleNotFoundError: No module named 'requests'

That 'python3.13' in the path is the giveaway. The pip that answered me belongs to Python 3.13. My project's Python is 3.15. The package went (or in my case, was already) in the wrong house.

The two commands

This is the whole diagnostic. Run both before you trust any 'pip install'.

1. Which pip answered?

which pip

You want a path inside your project, ending '.venv/bin/pip'. If it points at '/usr/bin', 'Library', 'AppData' or anything outside the project, you are about to install into something else. If it prints nothing, there is no pip on your PATH at all, which is the kind failure.

On Windows, the same job is done by 'where pip' in Command Prompt or 'Get-Command pip' in PowerShell. I only ran the Linux version, so check the output matches the same rule rather than taking my paths literally.

2. Does pip belong to the Python you'll run?

python -m pip --version

Two outcomes. Either it fails with 'No module named pip', meaning this Python has no pip, or it prints a line like this one, which I got from a seeded venv:

pip 26.2.2 from /tmp/uvt/seeded/.venv/lib/python3.15/site-packages/pip (python 3.15)

Read the end of that line: the version in brackets and the folder before it. They should match your project's Python and live inside your project, which here means the path runs through '.venv'. The habit works because 'python -m pip' asks the Python you named to run its own pip, so it can't wander off to another one. That makes it the safest spelling of the command, and the first thing to ask is always: 'python -m pip install', or just 'pip install'?

So the rule is short. If 'which pip' points outside your project, or 'python -m pip --version' names a different Python than the one that runs your script, stop. You've found the wrong Python before it cost you an afternoon. The cure is the next section: reach for 'uv add requests' instead of a bare 'pip install', and uv puts the package in the project's environment for you — no PATH guessing.

Why this matters

An error message at least tells you where to look. The silent version tells you nothing, and beginners read "Requirement already satisfied" as good news.

The failure this check prevents is installing into the wrong Python and then debugging your own code for something that was never your code's fault. I'd rather you spend ten seconds on two commands than an hour rewriting a correct import.

The three uv equivalents

Once you know what you're looking at, here are the uv commands that sidestep pip entirely. I ran each on the same demo project.

'uv add requests' adds the package to the project: it installs it and records it in 'pyproject.toml' under dependencies. This is the one to reach for in a project you'll keep.

dependencies = [
    "requests>=2.34.2",
]

'uv pip install requests' is the pip-compatible install. It puts the package into the environment but doesn't write it to 'pyproject.toml'. It targets the project's .venv, not whichever pip your PATH happens to find. I checked: after it, 'pyproject.toml' had no mention of requests, but the package imported fine from the venv. Handy for experiments, a trap if you forget to record the dependency.

'uv run python script.py' runs your script against the project's environment without you activating anything. 'uv run python -c "import requests; print(requests.version)"' printed '2.34.2' right after the install.

Rough guide: 'add' to keep it, 'pip install' to try it, 'run' to use it.

And if you'd rather keep pip

'uv venv --seed' puts pip inside the venv. I tried it and got 'pip 26.2.2' in '.venv/bin'. It works, but it makes the missing-tap problem disappear without teaching you to look. Run the two commands anyway.

Final thought

You don't have to switch from pip, but you do have to know which one you're talking to.

Your next step: open a terminal in any Python project, activate its environment, and run 'which pip' and 'python -m pip --version'. If the two don't agree, you've just found your next bug before it arrived.

Which pip Just Answered You? Two Commands Before You Trust pip install in a uv Project | Vibecodes