ModuleNotFoundError: No module named '...' almost never means what it says. Nine times out of ten the package is installed — just not in the interpreter that's running your code. So before you pip install the same thing a third time, spend ten seconds finding out which of about six causes you actually have. Then apply the one fix that matches.
The 10-second answer
Run these two commands in the same place your code runs (same terminal, same notebook cell):
python -c "import sys; print(sys.executable)"
python -m pip list
The first tells you exactly which Python is running. The second tells you whether the package is installed for that Python. If the package is missing from the list, you have an install problem. If it's in the list but the import still fails, you have an environment mismatch — you installed it for a different interpreter. That single check resolves most of these errors.
What the error actually means
ModuleNotFoundError arrived in Python 3.6 as a subclass of ImportError, raised specifically when Python can't locate a module. (On older versions you'd just get a plain ImportError.) Because it subclasses ImportError, your existing except ImportError: handlers still catch it.
The distinction matters for diagnosis:
ModuleNotFoundError— Python couldn't find the module. Not installed, or installed where this interpreter isn't looking.ImportError(without "ModuleNotFound") — Python found the module but it blew up while loading: a missing sub-dependency, a version clash, a broken compiled extension.
This post is about the first one.
How Python decides where to look
Every fix below follows from one mechanism. When you import x, Python checks built-in modules first, then walks the directories in sys.path in order, first match wins. That list is built at startup from:
- The directory of the script you ran (or the current directory in the REPL).
- Whatever is in the
PYTHONPATHenvironment variable. - The installation defaults, including
site-packages.
The consequence that explains nearly every mystery: a package installed into interpreter A's site-packages is completely invisible to interpreter B. Different Python, different site-packages, different answer to "is this installed?"

Diagnose before you fix
Three commands reveal the cause. Run each in the environment that's failing.
# 1. Which interpreter am I actually running?
python -c "import sys; print(sys.executable)"
# 2. Is the package installed FOR THIS interpreter?
python -m pip list
# 3. Where is this Python looking?
python -c "import sys; print(sys.path)"
If you hit the error inside VS Code or Jupyter — "it works in my terminal but not here" — the cause is almost always that the editor/kernel is pointed at a different interpreter than your terminal. In VS Code, check the interpreter in the status bar (Command Palette → Python: Select Interpreter). In Jupyter, run the sys.executable line inside a cell and compare it to your terminal's. They won't match, and that's your answer.
Fix by cause
Package not installed. The plain case. Install it into the running interpreter:
python -m pip install <package>
Wrong environment. You installed globally but you're running inside a virtual environment, or vice versa. Activate the venv first, then install through its Python:
source .venv/bin/activate # Windows: .venv\Scripts\activate
python -m pip install <package>
Multiple Python versions. Installed for 3.10, running on 3.12. Check what you're on with python --version, then target the right one explicitly:
python3.12 -m pip install <package>
Import name ≠ package name. You install one name and import another. This trips up everyone at least once:
You pip install… |
But you import… |
|---|---|
opencv-python |
cv2 |
Pillow |
PIL |
scikit-learn |
sklearn |
PyYAML |
yaml |
beautifulsoup4 |
bs4 |
Typo or wrong casing. Imports are case-sensitive. Re-read the import line — import Numpy is not import numpy.
Self-shadowing. You named your own file the same as the module you're importing. A local random.py sits in the script directory, which Python searches first, so import random grabs your file instead of the standard library — and then can't find the name it expected. Rename your file and delete any stray random.pyc / __pycache__. Same goes for email.py, queue.py, tokenize.py, and friends.
The habit that prevents most of this
Stop typing bare pip install. Use:
python -m pip install <package>
python -m pip runs pip through the exact interpreter you named, so the package always lands where that Python can see it. Bare pip is its own executable on PATH and may point at a completely different Python than the one running your script — which is the single biggest source of "I installed it but it's still not found." Pair that with a per-project virtual environment and the environment-mismatch class of errors mostly disappears.

A faster, mismatch-resistant option: uv
Worth knowing in 2026: uv from Astral (the team behind Ruff) is a Rust-based, all-in-one package and environment manager, around version 0.10 as of early 2026, advertised as 10–100× faster than pip. It manages the interpreter, the virtual environment, and dependencies together, which structurally removes a lot of the "which Python installed this?" guesswork.
uv venv
uv pip install <package>
# or, project-style:
uv add <package>
If you're starting something new, it's a clean default. For an existing pip workflow, the python -m pip habit above already covers you.
Version context
Current as of June 2026: the latest stable Python is 3.14.5 (released May 10, 2026); 3.13 is in maintenance (3.13.13); and 3.15 is in pre-release (3.15.0b2). If you maintain multiple versions side by side, the multi-version fix above is the one to bookmark.
Cheat sheet
Diagnose:
python -c "import sys; print(sys.executable)" # which Python
python -m pip list # installed here?
python -c "import sys; print(sys.path)" # where it looks
Cause → fix:
| Cause | Fix |
|---|---|
| Not installed | python -m pip install <package> |
| Wrong environment | activate the venv, then python -m pip install |
| Wrong Python version | python3.12 -m pip install <package> |
| Import vs package name | check the name table (cv2, PIL, sklearn…) |
| Typo / casing | re-read the import line |
| Self-shadowing file | rename your local *.py, clear __pycache__ |
Start with sys.executable and pip list. The mismatch they expose is the fix for most of these errors.
References
- Python docs — Modules and the import search path
- Python docs — Initialization of sys.path
- Brett Cannon — Why you should use
python -m pip - pip docs — Managing a different Python interpreter
- Towards Data Science — Fix ModuleNotFoundError and ImportError
- phoenixNAP — ModuleNotFoundError causes and solutions
- Status of Python versions — devguide

