How to Fix ImportError and ModuleNotFoundError in Python

Type: Software Reference Confidence: 0.93 Sources: 8 Verified: 2026-02-23 Freshness: quarterly

TL;DR

Constraints

Quick Reference

# 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]

Decision Tree

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]

Step-by-Step Guide

1. Read the exact error message

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.

2. Check if the package is installed

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.

3. Verify you're in the right Python environment

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.

4. Inspect sys.path

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.

5. Fix project structure

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.

6. Fix circular imports

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.

7. Fix name shadowing

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.

Code Examples

Robust import with fallback and diagnostics

# 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')

Import debugging utility

# 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')

Proper package setup with pyproject.toml

# 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

Anti-Patterns

Wrong: Installing packages globally

# BAD — installs into system Python, not your project's venv [src5, src7]
sudo pip install requests
pip install requests  # Which Python? Who knows!

Correct: Use virtualenv and python -m pip

# GOOD — explicit, uses correct Python [src5, src7]
python -m venv venv
source venv/bin/activate
python -m pip install requests

Wrong: Hacking sys.path in application code

# 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

Correct: Proper package structure with editable install

# GOOD — works everywhere, no sys.path hacking [src1, src5]
pip install -e .
# Now: from my_package.my_module import something

Wrong: Running script directly with relative imports

# BAD — running directly breaks relative imports [src3, src4]
python my_package/utils.py
# Error: ImportError: attempted relative import with no known parent package

Correct: Run as module

# GOOD — Python knows the package context [src3, src4]
python -m my_package.utils

Common Pitfalls

Diagnostic Commands

# 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 History & Compatibility

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]

When to Use / When Not to Use

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

Important Caveats

Related Units