ModuleNotFoundError (a subclass of ImportError)
means Python cannot locate the module you're trying to import. The 5 most common causes: (1) package not
installed (pip install), (2) wrong Python environment/virtualenv, (3) incorrect
sys.path / project structure, (4) circular imports between modules, (5) name shadowing
(your file has the same name as a library).python -c "import sys; print(sys.path)" shows exactly
where Python looks for modules. pip show package_name verifies installation location.
python -m pip install package ensures you install into the correct Python.pip install outside your virtualenv — the package
goes into the system Python while your code runs in the venv. Always check which python /
where python first. With uv, virtual environments do not include
pip by default.python -m pip install instead of bare pip install
— pip may point to a different Python interpreter than python, especially on
systems with multiple Python versions. [src5]sys.path.insert() in application code — use
pyproject.toml + pip install -e . for proper package resolution. sys.path
hacks break when directory structure changes. [src1, src2]__package__ and __cached__ — setting
these module attributes is deprecated and will cease to work in Python 3.14. Do not rely on them for
import logic. [src8]uv package
manager, use uv pip install or uv add instead of bare pip. Only
add pip when required for compatibility (e.g., Jupyter's %pip magic). [src5]random.py,
email.py, test.py, json.py, os.py will shadow the
real stdlib module and cause ImportError. Python 3.13 now warns about this explicitly. [src1, src8]| # | Cause | Likelihood | Signature | Fix |
|---|---|---|---|---|
| 1 | Package not installed | ~30% of cases | ModuleNotFoundError: No module named 'package' |
pip install package or python -m pip install package [src5]
|
| 2 | Wrong Python / virtualenv | ~20% of cases | pip install succeeds but import still fails |
Activate venv: source venv/bin/activate; verify: which python [src7] |
| 3 | Incorrect sys.path / project structure |
~15% of cases | ModuleNotFoundError for your own module |
Add __init__.py, fix project root, or use pip install -e . [src1, src2] |
| 4 | Circular import | ~10% of cases | ImportError: cannot import name 'X' from partially initialized module |
Refactor: move shared code to third module, or use local imports [src6] |
| 5 | Name shadowing (file shadows library) | ~8% of cases | ImportError: cannot import name 'X' from 'module' |
Rename your file (e.g., random.py -> my_random.py) [src1, src8]
|
| 6 | Relative import outside package | ~6% of cases | ImportError: attempted relative import with no known parent package |
Run as module: python -m package.module instead of
python module.py [src3, src4]
|
| 7 | Missing __init__.py |
~4% of cases | ModuleNotFoundError for subpackage |
Add empty __init__.py to directory [src1, src3] |
| 8 | Package name != import name | ~3% of cases | pip install succeeds, import name differs |
Check PyPI page for correct import name (e.g., pip install Pillow ->
import PIL) [src5]
|
| 9 | Python version mismatch | ~2% of cases | Package incompatible with Python version | Check package compatibility; use python --version [src5]
|
| 10 | IDE / editor path misconfiguration | ~2% of cases | Works in terminal but not in IDE | Set Python interpreter path in IDE settings [src2] |
START
├── Is the module a third-party package (from PyPI)?
│ ├── YES -> Is it installed? (pip show package_name)
│ │ ├── NOT INSTALLED -> pip install package_name [src5]
│ │ └── INSTALLED -> Is it in the right environment?
│ │ ├── Check: which python / where python -> matches venv? [src7]
│ │ │ ├── WRONG PYTHON -> Activate correct venv [src7]
│ │ │ └── CORRECT PYTHON -> Does import name match package name?
│ │ │ ├── NO -> Find correct import name (PyPI docs) [src5]
│ │ │ └── YES -> Check for name shadowing [src8]
│ │ └── IDE using different interpreter? -> Set interpreter in IDE [src2]
│ └── NO -> It's your own module ↓
├── Does the error say "partially initialized module" or "circular import"?
│ ├── YES -> Circular import. Refactor or use local imports [src6]
│ └── NO ↓
├── Does the error say "relative import with no known parent package"?
│ ├── YES -> Run as module: python -m package.module [src3, src4]
│ └── NO ↓
├── Is the module in a subdirectory?
│ ├── YES -> Does the directory have __init__.py? [src1]
│ │ ├── NO -> Add __init__.py
│ │ └── YES -> Is the parent directory in sys.path? [src2]
│ └── NO ↓
├── Using uv? -> uv pip install or uv add (pip not available by default) [src5]
│ └── NO ↓
└── Print sys.path and verify module location
-> python -c "import sys; print('\n'.join(sys.path))" [src2]
Python gives you precise information about what went wrong. Python 3.13+ provides even better error messages, including warnings when your file shadows a stdlib module. [src1, src8]
# ModuleNotFoundError — module doesn't exist in sys.path
ModuleNotFoundError: No module named 'requests'
# ImportError — module exists but the name inside it doesn't
ImportError: cannot import name 'Response' from 'my_module'
# ImportError — circular import
ImportError: cannot import name 'X' from partially initialized module 'Y'
# ImportError — relative import issue
ImportError: attempted relative import with no known parent package
# Python 3.13+ — name shadowing warning (new)
# "module 'random' has no attribute 'randint'" with hint:
# "consider renaming '/home/user/random.py'"
Verify: Note the exact module name and which pattern your error matches.
For third-party packages, verify installation first. [src5]
# Check if installed and where
pip show requests
# SAFEST: use python -m pip to ensure correct Python
python -m pip install requests
# If using uv (pip not available by default)
uv pip install requests
# Or better: uv add requests
Verify: pip show package_name shows the package with a Location
inside your virtualenv's site-packages.
The most frustrating cause: installing into system Python while running code in a virtualenv. [src7]
# Check which Python is active
which python # Linux/macOS
where python # Windows
# Activate virtualenv
source venv/bin/activate # Linux/macOS
.\venv\Scripts\Activate.ps1 # Windows PowerShell
# If using uv
uv venv
source .venv/bin/activate # Linux/macOS
# Verify after activation
python -c "import sys; print(sys.executable)"
Verify: sys.executable points to your virtualenv's Python binary.
Python searches for modules in the directories listed in sys.path. [src1, src2]
# Print sys.path to see all search directories
import sys
print('\n'.join(sys.path))
# Check where an imported module actually comes from
import requests
print(requests.__file__)
Verify: The directory containing your module appears in sys.path.
Proper Python package structure ensures imports work correctly. [src1, src3]
my_project/
├── pyproject.toml
├── src/
│ └── my_package/
│ ├── __init__.py # Makes it a package
│ ├── main.py
│ ├── utils.py
│ └── sub_package/
│ ├── __init__.py # Required for subpackage
│ └── helpers.py
├── tests/
│ ├── __init__.py
│ └── test_main.py
└── venv/
# For development: install your package in editable mode
pip install -e .
Verify: Run python -c "from my_package import main" from the project root.
Circular imports happen when module A imports from B and B imports from A. [src6]
# FIX 1: Move shared code to a third module
# shared.py (new)
class User: ...
# FIX 2: Use local (deferred) imports
def get_user_service():
from models import User # Import inside function
return User.objects.get(...)
# FIX 3: Import the module, not the name
import models # Instead of: from models import User
def get_user_service():
return models.User.objects.get(...)
Verify: python -c "import models; import services" works without errors.
If your file has the same name as a library, Python imports your file instead. Python 3.13 now provides explicit warnings for this. [src1, src8]
# Check if you're shadowing a module
python -c "import random; print(random.__file__)"
# If it shows YOUR file -> NAME CONFLICT!
# Common offenders: random.py, email.py, test.py, json.py, os.py
# Fix: Rename your file, then clean cache
find . -name "__pycache__" -type d -exec rm -rf {} +
Verify: python -c "import random; print(random.__file__)" points to the stdlib.
# Input: Script needs to run in different environments
# Output: Graceful handling of missing imports with helpful error messages
import sys
import importlib
def safe_import(module_name, package_name=None, install_hint=None):
"""Try to import a module, providing helpful error if it fails."""
try:
return importlib.import_module(module_name)
except ImportError as e:
pkg = package_name or module_name
hint = install_hint or f"pip install {pkg}"
print(f"ERROR: Could not import '{module_name}'", file=sys.stderr)
print(f" Python executable: {sys.executable}", file=sys.stderr)
print(f" To fix: {hint}", file=sys.stderr)
raise SystemExit(1) from e
# Common packages where install name != import name
requests = safe_import('requests')
PIL = safe_import('PIL', package_name='Pillow')
cv2 = safe_import('cv2', package_name='opencv-python')
yaml = safe_import('yaml', package_name='PyYAML')
sklearn = safe_import('sklearn', package_name='scikit-learn')
# Input: Can't figure out why an import is failing
# Output: Diagnostic script that pinpoints the problem
import sys, os, importlib
def diagnose_import(module_name):
"""Diagnose why a module can't be imported."""
print(f"=== Import Diagnosis for '{module_name}' ===\n")
print(f"Python version: {sys.version}")
print(f"Executable: {sys.executable}")
print(f"Virtual env: {sys.prefix != sys.base_prefix}")
print(f"\nsys.path:")
for i, p in enumerate(sys.path):
exists = "OK" if os.path.exists(p) else "MISSING"
print(f" [{i}] {exists} {p}")
try:
mod = importlib.import_module(module_name)
print(f"\nImport succeeded!")
print(f" Location: {getattr(mod, '__file__', 'built-in')}")
return mod
except (ModuleNotFoundError, ImportError) as e:
print(f"\n{type(e).__name__}: {e}")
# Check for name shadowing
shadow = os.path.join(os.getcwd(), module_name.split('.')[0] + '.py')
if os.path.exists(shadow):
print(f"\nNAME SHADOWING: Found '{shadow}'!")
return None
if __name__ == '__main__':
diagnose_import(sys.argv[1] if len(sys.argv) > 1 else 'requests')
# Input: Project where imports break from different directories
# Output: pyproject.toml that makes imports always work
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.backends._legacy:_Backend"
[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.9"
dependencies = [
"requests>=2.28",
"pydantic>=2.0",
]
[project.optional-dependencies]
dev = ["pytest>=7.0", "ruff>=0.1"]
[tool.setuptools.packages.find]
where = ["src"]
# Install in editable mode:
pip install -e ".[dev]"
# Now imports work from ANYWHERE
# BAD — installs into system Python, not your project's venv [src5, src7]
sudo pip install requests
pip install requests # Which Python? Who knows!
# GOOD — explicit, uses correct Python [src5, src7]
python -m venv venv
source venv/bin/activate
python -m pip install requests
# BAD — fragile, breaks when directory structure changes [src2]
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'lib'))
from my_module import something
# GOOD — works everywhere, no sys.path hacking [src1, src5]
pip install -e .
# Now: from my_package.my_module import something
# BAD — running directly breaks relative imports [src3, src4]
python my_package/utils.py
# Error: ImportError: attempted relative import with no known parent package
# GOOD — Python knows the package context [src3, src4]
python -m my_package.utils
pip install vs python -m pip install: Just pip
might point to a different Python than python. Always use
python -m pip install to guarantee you install into the Python you're actually running. [src5]Pillow -> PIL, opencv-python -> cv2,
beautifulsoup4 -> bs4, scikit-learn -> sklearn,
PyYAML -> yaml. Check the PyPI page. [src5]__init__.py: Python 3 has namespace packages (no
__init__.py needed), but regular packages still need it for most use cases. Add an empty
__init__.py to every directory you want as a package. [src1, src3]test.py: A file named test.py shadows
Python's built-in test module and can break unittest. Other common shadows:
email.py, random.py, json.py, string.py. Python
3.13 now warns about this explicitly. [src1, src8].pyc / __pycache__: After renaming or moving files, old
bytecode cache may confuse imports. Delete with
find . -name "__pycache__" -exec rm -rf {} +. [src1]sys.path.insert(0, '/path/to/project') or use
%cd /path/to/project. [src2]uv,
pip is not installed by default. Use uv pip install or
uv add instead. Only add pip for Jupyter %pip compatibility. [src5]__package__ deprecation: Setting __package__ or
__cached__ on a module is deprecated in 3.13 and will stop working in 3.14. Migrate away
from any code that sets these attributes. [src8]# Check which Python is active
which python # Linux/macOS
where python # Windows
# Check if in virtualenv
python -c "import sys; print('venv' if sys.prefix != sys.base_prefix else 'system')"
# Print sys.path
python -c "import sys; print('\n'.join(sys.path))"
# Check if package is installed and where
pip show package_name
python -m pip show package_name
# Check where a module is loaded from
python -c "import module_name; print(module_name.__file__)"
# Find name shadowing
python -c "
import os, sys
stdlib = set(os.listdir(os.path.dirname(os.__file__)))
local = set(f for f in os.listdir('.') if f.endswith('.py'))
shadows = local & {s + '.py' for s in stdlib if not s.startswith('_')}
if shadows: print('Shadowing:', shadows)
else: print('No shadows found')
"
# Clean bytecode cache
find . -name "__pycache__" -type d -exec rm -rf {} +
find . -name "*.pyc" -delete
| Version | Behavior | Key Changes |
|---|---|---|
| Python 3.13+ (Oct 2024) | ModuleNotFoundError (enhanced) |
Better name-shadowing warnings; __package__/__cached__ deprecated;
from .__future__ no longer triggers future statements [src8] |
| Python 3.12 | ModuleNotFoundError (standard) |
Improved error messages with suggestions for similar module names;
pkgutil.find_loader() deprecated [src1] |
| Python 3.11 | ModuleNotFoundError |
Exception groups; better tracebacks for import errors [src1] |
| Python 3.10 | ModuleNotFoundError |
importlib.metadata improvements; better error messages [src1] |
| Python 3.9 | ModuleNotFoundError |
importlib.resources updates [src1] |
| Python 3.6 | ModuleNotFoundError introduced |
New subclass of ImportError for missing modules [src1] |
| Python 3.3 | Namespace packages | __init__.py no longer required for namespace packages [src1] |
| Python 2.x | ImportError only |
No ModuleNotFoundError; different import semantics [src1] |
| Use When | Don't Use When | Use Instead |
|---|---|---|
Error says ModuleNotFoundError or ImportError |
Error is AttributeError after successful import |
Check the object name in the module |
import statement fails |
Error is SyntaxError in the imported module |
Fix syntax in the target module |
| Package installs but import still fails | Error is PermissionError during import |
Check file permissions |
| Error mentions "partially initialized module" | Error is ValueError from module code |
Debug the module's runtime logic |
| Module works in terminal but not in IDE | Error is DLL load failed or shared library missing |
Reinstall package with binary deps |
ModuleNotFoundError is a subclass of ImportError. Catching
ImportError will also catch ModuleNotFoundError. Use the more specific type
when you only want to handle missing modules.__init__.py, but only for
specific use cases. For regular projects, always include __init__.py.pip install --user installs to ~/.local/lib/pythonX.Y/site-packages/, which
may not be in sys.path for all Python invocations.importlib.reload(module) can re-import a module but won't fix circular imports — it may
make them worse by re-triggering the import cycle.__package__ and __cached__
module attributes entirely. Code that relies on setting these during import will break.