ModuleNotFoundError: No module named '...' has a handful of distinct causes, and they need different fixes. Reinstalling the package fixes only one of them. To write this guide I set up five of the six causes in a throwaway folder on my Mac and kept the exact terminal output: the error, the command that identifies the cause, and the output after the fix. Everything below was run on macOS 26.6.2 with Python 3.14.6, 3.12.14 and the system 3.9.6, pip 26.1.2 and uv 0.11.24. Full versions are at the end.
Which one do you have?
| What you see | Most likely cause | Section |
|---|---|---|
pip install X succeeded, but import X still fails |
The package went into a different interpreter than the one running your code | 1 |
uv run main.py works, python3 main.py doesn't |
The project's virtual environment isn't being used | 2 |
No module named pip inside a virtual environment |
It was created by uv venv, which doesn't install pip |
2 |
No module named 'python_decouple', 'pil', 'Rich' |
The import name differs from the install name, or the case is wrong | 3 |
No module named 'X.something'; 'X' is not a package |
A file of yours called X.py hides the real package |
4 |
No module named 'core' (your own folder), or attempted relative import with no known parent package |
You ran a file inside a package as a script | 5 |
| Works in the terminal, fails in Jupyter or VS Code | The editor or kernel uses another interpreter | 6 |
Three commands that tell you which cause it is
Run these with the exact command you use to run your code (python3, python, .venv/bin/python, uv run python), in the same directory. Replace requests with your module name.
python3 -c "import sys; print(sys.executable)"
python3 -m pip --version
python3 -c "import importlib.util as u; s = u.find_spec('requests'); print(s.origin if s else None)"
How to read the results:
sys.executableis not the Python you installed into. You have cause 1 or 2.find_specprintsNone. This interpreter can't see the module. Either it's installed somewhere else (cause 1 or 2), or the name is wrong (cause 3).find_specprints a path inside your project, like/path/to/project/requests.py. You have cause 4. In my lab the same command printed~/lab/c4/rich.pywith the shadowing file present and.../site-packages/rich/__init__.pyonce I removed it.- The missing module is a folder of your own project. You probably have cause 5. To see where a script's imports start, put
import sys; print(sys.path[0])at the top of the script itself and run it the way you normally do. In my lab,scripts/where.pyprinted~/lab/c5/myproj/scripts, not the project root. (Running that line withpython3 -conly prints'', the current directory, so it can't tell you this.)
find_spec only looks the module up. For a top-level name it doesn't run the module's code, so it's safe to use even when importing the module crashes.
1. Installed into a different Python
On this Mac, which -a python3 finds two interpreters before any virtual environment is involved: Homebrew's 3.14.6 at /opt/homebrew/bin/python3 and Apple's 3.9.6 at /usr/bin/python3. There is also a python3.12, and there is no plain python command at all. Each interpreter has its own site-packages. Installing into one tells the others nothing.
Reproduced. I created a venv with Homebrew's 3.14, activated it, installed requests, and ran the same file three ways:
~/lab/c1 $ python app.py
requests 2.34.2
~/lab/c1 $ python3.12 app.py
Traceback (most recent call last):
File "~/lab/c1/app.py", line 1, in <module>
import requests
ModuleNotFoundError: No module named 'requests'
~/lab/c1 $ /usr/bin/python3 app.py
Traceback (most recent call last):
File "~/lab/c1/app.py", line 1, in <module>
import requests
ModuleNotFoundError: No module named 'requests'
Diagnosis. Inside the activated venv, python points into .venv, but python3.12 still resolves to Homebrew's interpreter:
~/lab/c1 $ python3.12 -c "import sys; print(sys.executable)"
/opt/homebrew/opt/python@3.12/bin/python3.12
~/lab/c1 $ python -m pip show requests | grep -E "^(Name|Version|Location)"
Name: requests
Version: 2.34.2
Location: ~/lab/c1/.venv/lib/python3.14/site-packages
~/lab/c1 $ python3.12 -m pip show requests
WARNING: Package(s) not found: requests
Fix. Run the code with the interpreter that has the package (python app.py inside the venv, or .venv/bin/python app.py without activating). If you really need a different Python, install through that interpreter: <that-python> -m pip install requests. The pip user guide says why this works: "python -m pip executes pip using the Python interpreter you specified as python."
The copied-venv trap
The second way to install into the wrong place is easy to miss. I created a venv in proj-old, copied the whole project to proj-new with cp -R, activated the copy and installed rich. Everything appeared to work, but every command was quietly using the old environment:
~/lab/c1b/proj-new $ pip --version
pip 26.1.2 from ~/lab/c1b/proj-old/.venv/lib/python3.14/site-packages/pip (python 3.14)
~/lab/c1b/proj-new $ head -1 .venv/bin/pip
#!/…/lab/c1b/proj-old/.venv/bin/python3.14
The activate script and the pip launcher both have the original absolute path written into them. Once something launched the copy's own interpreter directly, as a tool that runs proj-new/.venv/bin/python would, the import failed:
~/lab/c1b/proj-new $ .venv/bin/python app.py
Traceback (most recent call last):
File "~/lab/c1b/proj-new/app.py", line 1, in <module>
from rich import print
ModuleNotFoundError: No module named 'rich'
Fix. Delete the copied .venv and create a new one. The venv documentation says environments are "not considered as movable or copyable – you just recreate the same environment in the target location."
2. The virtual environment is not being used
Reproduced. I created a uv project, added requests, and ran it the way muscle memory suggests:
~/lab/c2/demo $ uv add -q requests
~/lab/c2/demo $ python3 main.py
Traceback (most recent call last):
File "~/lab/c2/demo/main.py", line 1, in <module>
import requests
ModuleNotFoundError: No module named 'requests'
~/lab/c2/demo $ uv run main.py
requests 2.34.2
Diagnosis. uv add installs into the project's .venv. Nothing is activated, so python3 is still Homebrew's interpreter:
~/lab/c2/demo $ python3 -c "import sys; print(sys.executable)"
/opt/homebrew/opt/python@3.14/bin/python3.14
~/lab/c2/demo $ uv run python -c "import sys; print(sys.executable)"
~/lab/c2/demo/.venv/bin/python3
Fix. Use uv run, which, according to the uv docs, makes sure "the project environment is up-to-date before running the given command". Alternatively, call .venv/bin/python main.py directly, or source .venv/bin/activate first. The same applies to a plain python3 -m venv environment: if it isn't activated and you don't call its bin/python, it isn't used.
"No module named pip" inside a uv venv
After activating that uv environment, the advice from section 1 failed:
~/lab/c2/demo $ python -m pip install requests
~/lab/c2/demo/.venv/bin/python: No module named pip
uv creates environments without pip. The uv CLI reference lists --seed to "install seed packages (one or more of: pip, setuptools, and wheel)". In a uv environment, use uv pip install or uv add. Alternatively, create the environment with uv venv --seed if you want python -m pip to work.
There is a worse version of this. Because the uv venv has no pip3 of its own, pip3 fell through to the next one on PATH, which was Homebrew's global pip:
~/lab/c2/demo $ which -a pip pip3 | head -3
pip not found
/opt/homebrew/bin/pip3
/usr/bin/pip3
In my lab, the PIP_REQUIRE_VIRTUALENV=1 variable I set as a safety guard stopped it with ERROR: Could not find an activated virtualenv (required). Without that guard, Homebrew's Python is marked as externally managed (PEP 668). Its marker file on this machine tells pip to refuse and to suggest a virtual environment. I didn't run pip against Homebrew's Python to see it. Either way, the package doesn't end up in your project.
3. The import name is not the install name
The name you pip install (the distribution) and the name you import don't have to match. The Python docs mention packages_distributions() as the way to map one to the other.
Reproduced. I installed python-decouple and imported it by its install name:
~/lab/c3 $ python -c "import python_decouple"
Traceback (most recent call last):
File "<string>", line 1, in <module>
import python_decouple
ModuleNotFoundError: No module named 'python_decouple'
~/lab/c3 $ python -c "from decouple import config; print(config)"
<decouple.AutoConfig object at 0x1075e5400>
Diagnosis. Ask pip which files the distribution installed, or ask Python for the full mapping:
~/lab/c3 $ python -m pip show -f python-decouple | sed -n '/^Files:/,$p'
Files:
__pycache__/decouple.cpython-314.pyc
decouple.py
python_decouple-3.8.dist-info/INSTALLER
...
~/lab/c3 $ python -c "from importlib.metadata import packages_distributions as pd; ..."
{'pygments': ['Pygments'], 'rich': ['rich'], 'mdurl': ['mdurl'], 'decouple': ['python-decouple'], 'markdown_it': ['markdown-it-py'], 'pip': ['pip']}
If you see No module named 'decouple', the fix is pip install python-decouple, not pip install decouple. On PyPI, decouple is an unrelated project ("Decoupling logic", version 0.0.7). I downloaded its source archive without installing it, and it also ships a top-level decouple/ package, so installing the wrong name gives you a module called decouple with a different API.
The reverse mistake fails at install time, which at least is obvious:
~/lab/c3 $ python -m pip install PIL
ERROR: Could not find a version that satisfies the requirement PIL (from versions: none)
ERROR: No matching distribution found for PIL
Common pairs to install versus import: Pillow → PIL (Pillow's own examples use from PIL import Image), opencv-python → cv2, beautifulsoup4 → bs4, scikit-learn → sklearn, PyYAML → yaml. When I checked pip install cv2 separately, it failed the same way as PIL above.
Case matters, even on a case-insensitive disk
My Mac's APFS volume is case-insensitive: ls -d site-packages/Rich finds the rich folder. The import system still compared the case and refused:
~/lab/c3 $ python -c "from Rich import print"
Traceback (most recent call last):
File "<string>", line 1, in <module>
from Rich import print
ModuleNotFoundError: No module named 'Rich'
That was Python 3.14.6. Python 3.15, released on 2026-10-09, adds a hint to the same error: Did you mean: 'rich'?
So from pil import Image fails even with Pillow installed. It must be PIL, in capitals. (I didn't install Pillow, which is a compiled package; rich showed the same behaviour.)
4. A file of yours hides the package
Reproduced. I put a test script named rich.py next to an environment that has rich installed:
~/lab/c4 $ python rich.py
Traceback (most recent call last):
File "~/lab/c4/rich.py", line 1, in <module>
from rich.console import Console
File "~/lab/c4/rich.py", line 1, in <module>
from rich.console import Console
ModuleNotFoundError: No module named 'rich.console'; 'rich' is not a package
The file appears twice in the traceback because it ran as your script and then imported itself as rich. As the sys.path documentation explains, "the first entry in the module search path is the directory that contains the input script, if there is one", so rich.py is found before site-packages. A single file isn't a package, so rich.console can't exist. That is what "is not a package" means.
Diagnosis. find_spec('rich') printed ~/lab/c4/rich.py instead of a site-packages path. See the diagnostic block above.
Fix. Rename the file (deleting the __pycache__ next to it is optional tidying):
~/lab/c4 $ mv rich.py demo_rich.py
~/lab/c4 $ python demo_rich.py
ok
If you use only the top-level name, the error is different. With a local requests.py that calls requests.get, Python 3.14 pointed straight at the problem:
AttributeError: module 'requests' has no attribute 'get' (consider renaming '~/lab/c4/requests.py' if it has the same name as a library you intended to import)
Python 3.12 sent me in the wrong direction on the same file:
AttributeError: partially initialized module 'requests' has no attribute 'get' (most likely due to a circular import)
The clearer message arrived in 3.13 (What's New), for both standard-library and third-party names. On 3.12 or older, a "circular import" message about a library you didn't write usually means a file of yours has the library's name.
Python 3.11+ also has -P, which, according to the command-line docs, means "Don't prepend a potentially unsafe path to sys.path". With the shadowing rich.py still in place, python -P rich.py printed ok. That's useful for a quick check, but renaming is the real fix.
5. Running a file inside your own package
This produces errors like No module named 'core', 'base', 'scripts' or 'modules', where the missing module is a folder in your own project.
Reproduced. I used this layout:
myproj/
├── core/
│ ├── __init__.py
│ ├── base.py # DEBUG = True
│ └── settings.py # from .base import DEBUG
└── scripts/
└── seed.py # from core import settings
~/lab/c5/myproj $ python3 scripts/seed.py
Traceback (most recent call last):
File "~/lab/c5/myproj/scripts/seed.py", line 1, in <module>
from core import settings
ModuleNotFoundError: No module named 'core'
~/lab/c5/myproj $ python3 core/settings.py
Traceback (most recent call last):
File "~/lab/c5/myproj/core/settings.py", line 1, in <module>
from .base import DEBUG
ImportError: attempted relative import with no known parent package
Diagnosis. When you run a path, the script's own folder becomes sys.path[0], not the project root:
~/lab/c5/myproj $ python3 scripts/where.py
sys.path[0] = ~/lab/c5/myproj/scripts
core/ is not inside scripts/, so Python can't find it. In the second case, a file run as a script has no parent package, so .base means nothing.
Fix. From the project root, run it as a module with dots instead of slashes and no .py:
~/lab/c5/myproj $ python3 -m scripts.seed
settings loaded, DEBUG = True
seeded
~/lab/c5/myproj $ python3 -m core.settings
settings loaded, DEBUG = True
With -m, "the current directory will be added to the start of sys.path" (command-line docs), so it must be the project root. From inside scripts/, the same command failed:
~/lab/c5/myproj/scripts $ python3 -m scripts.seed
/opt/homebrew/opt/python@3.14/bin/python3.14: Error while finding module specification for 'scripts.seed' (ModuleNotFoundError: No module named 'scripts')
PYTHONPATH=.. python3 seed.py also worked from inside scripts/. I'd use it only for one-off runs, because it depends on where you are standing. For anything permanent, run from the root with -m, or make the project installable.
6. Jupyter and VS Code
Not reproduced. I didn't reproduce this headlessly, because installing Jupyter goes beyond the small pure-Python packages I limited this lab to. The mechanism is the same as in section 1: the kernel or editor runs its own interpreter.
- Jupyter: run
import sys; print(sys.executable)in a cell and compare it with your terminal. To install into the kernel that is actually running, use the%pipmagic, which IPython describes as "Run the pip package manager within the current kernel." - VS Code: the Python environments docs say the interpreter is chosen with Python: Select Interpreter or from the status bar, and "the selected environment is used for running code, debugging, and language features". Point it at the project's
.venv/bin/python.
Windows note: I didn't test Windows. The pip user guide gives py -m pip <pip arguments> as the Windows form of python -m pip, and the same rule applies.
What I'd do by default
- Install with
python -m pip, or withuv addin uv projects. Never use a barepipwhose interpreter you haven't checked. - Run with the interpreter you installed into. That means
uv run, an activated venv, or.venv/bin/python. - Recreate venvs instead of copying them.
- Don't name files after the libraries they import.
- Run your own packages from the project root with
python -m.
How I reproduced this
- Machine: Apple Silicon (arm64) Mac, macOS 26.6.2 (build 25G83), APFS boot volume (case-insensitive). Run on 2026-10-04.
- Interpreters: Homebrew Python 3.14.6 (
/opt/homebrew/bin/python3) and 3.12.14, Apple's/usr/bin/python33.9.6, and CPython 3.14.6 inside the uv project's environment. pip 26.1.2 in the 3.14 venvs, pip 26.2.1 in Homebrew 3.12, uv 0.11.24. - Packages: requests 2.34.2, rich 15.0.0, python-decouple 3.8, all installed only into throwaway virtual environments.
decouple0.0.7 was downloaded as a source archive and listed, not installed. - Isolation: every command ran with
PIP_REQUIRE_VIRTUALENV=1, so pip refused to install into any global interpreter. No global Python,PATHor shell configuration was changed. - Output: copied from the raw session log. The lab directory path is shortened to
~/lab, two long outputs (thepip show -ffile list and thepackages_distributionscommand) are abbreviated with..., and nothing else is edited. - Not reproduced: Jupyter, VS Code and Windows (sourced above), and Homebrew's PEP 668 refusal (I read the marker file rather than run pip against Homebrew's Python).
References
- Python docs:
ModuleNotFoundError, initialization ofsys.path, the import system, command line-mand-P,venv,importlib.metadata, What's New in 3.13 - pip: Running pip
- Python Packaging User Guide: Installing packages, externally managed environments, PEP 668
- uv: running commands in projects,
uv venv --seed - IPython
%pipmagic, VS Code Python environments - PyPI: python-decouple, decouple; Pillow Image module



