Python

How to Fix "ModuleNotFoundError: No Module Named" in virtualenv / venv

2 min read by DebuggedIt

Quick answer

You install a package with pip and it appears to succeed, but your script still can't find it. This almost always means the package was installed into a...

You install a package with pip and it appears to succeed, but your script still can't find it. This almost always means the package was installed into a different Python environment than the one actually running your script β€” a mismatch that's extremely common once virtual environments enter the picture.

The Problem

The install looks fine, but running the script fails immediately:

$ pip install requests
Successfully installed requests-2.32.3

$ python script.py
Traceback (most recent call last):
  File "script.py", line 1, in <module>
    import requests
ModuleNotFoundError: No module named 'requests'
Two separate Python environments System Python pip install ran here venv Python script.py ran here β€” no package Fix: activate the venv BEFORE both pip install and python script.py

Why It Happens

Every Python virtual environment has its own isolated set of installed packages, completely separate from the system Python and from any other virtualenv on the machine. A package installed while one environment is active is invisible to any other environment, including the plain system Python you'd get by typing python without any environment activated. This error appears when:

  • The virtualenv wasn't actually activated when you ran pip install, so the package went into the system Python (or a different environment) instead of the one your script uses.
  • The virtualenv was activated for the install but isn't active anymore when you run the script β€” for example, in a new terminal tab that starts fresh without the environment activated.
  • Multiple Python installations exist on the machine (a system Python, a Homebrew Python, a pyenv-managed version), and pip and python resolve to different ones depending on PATH ordering.
  • An IDE or editor's "Run" button is configured to use a different Python interpreter than the terminal you used for the install.

The Fix

First, confirm whether your virtualenv is actually active β€” an active venv shows its name in the shell prompt, and you can verify explicitly:

which python
/usr/bin/python3   # <- this is the SYSTEM python, not a venv

If you expected a venv path like /home/user/myproject/venv/bin/python and instead see a system path, the environment isn't active. Activate it explicitly:

source venv/bin/activate

On Windows:

venv\Scripts\activate

Confirm activation worked:

which python
/home/user/myproject/venv/bin/python

Now install the package with the venv active, and it will land in the right place:

pip install requests
python script.py

To avoid this mismatch entirely, a more robust habit is invoking pip and python through the same explicit interpreter path rather than relying on activation and PATH resolution being correct in every terminal session:

./venv/bin/python -m pip install requests
./venv/bin/python script.py

Using python -m pip instead of a bare pip command guarantees the package installs into whichever Python that specific python command refers to, removing any ambiguity about which pip executable happened to be first on your PATH.

Still Not Working?

If activation looks correct and which python points at the venv, but the import still fails, double-check you're not accidentally running the script with a different interpreter than the one you tested with β€” this is common in editors and IDEs where the terminal and the "Run" button can be configured independently:

python -c "import sys; print(sys.executable)"

Compare this output against the interpreter path your editor is configured to use for running scripts (in VS Code, check the Python interpreter selector in the bottom status bar). If they differ, update your editor's configured interpreter to match the venv you've been installing packages into.

It's also worth understanding why venvs exist in the first place, since the isolation they provide is the whole point rather than an inconvenience to work around. Different projects often need different, sometimes conflicting, versions of the same package β€” one project might need an older version of a library for compatibility, while another needs the latest release for a new feature. Without virtual environments, installing packages globally (or with sudo pip install, which is worth avoiding entirely) means every project on the machine shares one single set of package versions, and upgrading a package for one project can silently break another that depended on the older behavior:

# A clean, deliberate workflow for every new project
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Getting into the habit of creating a fresh venv per project, and always double-checking it's active before running pip or python, eliminates this entire class of confusing "it worked yesterday" errors well before they have a chance to happen.