Home Python ModuleNotFoundError: Fix Python Imports That Fail
Beginner 5 min · September 23, 2026

ModuleNotFoundError: Fix Python Imports That Fail

ModuleNotFoundError means Python searched sys.path and found no module by that name.

N
Naren Founder & Principal Engineer

20+ years shipping production Python across data and backend systems. Written from production experience, not tutorials.

Follow
Production
production tested
September 23, 2026
last updated
1,905
articles · all by Naren
Before you start⏱ 13 min
  • Running Python scripts and installing packages with pip
  • Basic virtual environments and the command line
  • Familiarity with tracebacks and import statements
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • The fix: install with the interpreter that runs your app via python3 -m pip install , then confirm with python3 -m pip show .
  • Check the mismatch with which python3 && python3 -c "import sys; print(sys.executable)"; bare pip often belongs to a different environment.
  • Launch package code with python3 -m pkg.mod from the project root so sys.path[0] includes the package — python3 pkg/mod.py does not.
  • Rule out shadowing with python3 -c "import ; print(.__file__)"; a local random.py or email.py hijacks the real module.
✦ Definition~90s read
What is Python ModuleNotFoundError Fix?

ModuleNotFoundError is the exception the import system raises when no finder on sys.path locates the requested top-level module. Concretely, import requests walks the finders for sys.path entries in order; if every finder declines, the machinery raises ModuleNotFoundError: No module named 'requests', attaching the missing name as exc.name (and the dotted path as exc.path for submodule misses).

Think of Python's import system as a librarian with a fixed list of shelves: your script's folder first, then PYTHONPATH entries, then the site-packages stacks.

It subclasses ImportError, which makes except ImportError catch it — a convenience that also causes misdiagnosis when overused. What it is NOT is equally diagnostic. It is not the error for a found module that crashes during execution — that surfaces as whatever the module raised, or as ImportError for circular imports through partially initialized modules.

It is not from pkg import missing_name failing on a name inside an existing package — that's plain ImportError (cannot import name). It is not a syntax error, a missing attribute, or a pip failure; pip failing means nothing was installed, while this error means the interpreter searched and found nothing.

So the name in the message is a precise work order: either put that top-level module where this interpreter searches, or point this interpreter at the search list where the module already sits.

Plain-English First

Think of Python's import system as a librarian with a fixed list of shelves: your script's folder first, then PYTHONPATH entries, then the site-packages stacks. ModuleNotFoundError means the librarian walked every shelf and the book wasn't there. Usually the book exists in a different branch — shelved downtown (one interpreter) while the librarian searched uptown (your venv).

You installed the package — you watched pip succeed — and Python still says it doesn't exist. ModuleNotFoundError: No module named 'requests' is the interpreter telling you it searched every directory it knows and found nothing by that name. The search list is sys.path: the script's folder, PYTHONPATH entries, and the environment's site-packages. When any link in that chain points somewhere unexpected, the import fails no matter how correctly you spelled it. This error fires in five familiar situations: pip installed into one interpreter while the app runs under another, a script launched as python pkg/mod.py instead of python -m pkg.mod so the package root never lands on the path, a PYTHONPATH that exists on your laptop but not the server, a local random.py or email.py shadowing the standard library, and a package missing its __init__.py on tooling that still expects one. Each shape has a one-command confirmation — which python3, python -m pip show, printing sys.path or a module's __file__ — and a permanent fix that stops it recurring. This article walks through the lookup mechanism, the interpreter-mismatch trap, the -m package context, stdlib shadowing, namespace packages, and how this error differs from its parent ImportError.

sys.path Missed: Where Python Looked Before Giving Up

Every import starts with a search, and sys.path is the list of places Python agrees to look. In order: the launching context (sys.path[0], covered in the -m section), entries from the PYTHONPATH environment variable, the standard library, and the current environment's site-packages. The import system asks each location's finder for the top-level module name, takes the first hit, and only when every finder declines does it raise ModuleNotFoundError — carrying the missing name on exc.name so your handler can read it programmatically. That search order explains most mysteries. The same import requests succeeds in one terminal and fails in another because the two interpreters own different site-packages folders, or because one launch put the project root on the path and the other didn't. Your first diagnostic is always a dump from inside the failing context: python3 -c "import sys, pprint; pprint.pp(sys.path)" shows exactly which shelves the librarian checked. Compare that against where the package actually lives (python3 -m pip show <pkg> prints Location:), and the gap — wrong environment, missing root, absent PYTHONPATH — is usually visible in seconds rather than hours.

modulenotfound_sys_path.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
import sys

print("interpreter:", sys.executable)
print("entry 0:", sys.path[0])
print("search list:")
for p in sys.path[:4]:
    print(" -", p)

try:
    import _definitely_missing_module_xyz
except ModuleNotFoundError as exc:
    print("missing name:", repr(exc.name))
    print("message:", exc)
📊 Production Insight
Containers make this check decisive. Exec into the running container and dump sys.path there — the image build log describes a different filesystem than the crashed process sees.
🎯 Key Takeaway
Dump sys.path inside the failing context and compare it with the package's installed Location — the gap names the fix.

pip Installed It, Python Can't See It: The Interpreter Mismatch

The single most common cause of this error is also the most embarrassing: the package is installed, just not for the interpreter running your code. pip is not a universal installer — it's a script pinned to one specific Python. On a machine with a system Python, a Homebrew Python, and two virtual environments, pip, pip3, and python3 can easily belong to three different worlds. pip install requests succeeding while python3 -c "import requests" fails is the signature symptom, and it confuses people because both commands look authoritative. The fix is a habit change: never address pip directly. python3 -m pip install requests runs pip as a module inside that exact interpreter, so installer and importer cannot disagree. Diagnose the current mess with which -a python3 pip pip3 (multiple lines means multiple worlds) and python3 -m pip show <pkg> versus pip show <pkg> — when the two answers differ, you've found your split. Virtual environments sharpen the trap because an inactive venv leaves your shell on the system Python while your service file points at the venv, or vice versa. Spell the interpreter absolutely everywhere it matters: Dockerfiles, systemd units, cron lines.

modulenotfound_which_python.pyPYTHON
1
2
3
4
5
6
7
import sys
import site

print("executable:", sys.executable)
print("prefix:", sys.prefix)
print("site-packages:", site.getsitepackages()[0])
print("pair me with: python -m pip")
💡Ban Bare pip in Docs and Dockerfiles
If your docs, Dockerfiles, or runbooks contain a bare pip install, rewrite it as python -m pip install with the project's interpreter. One habit removes the most common ModuleNotFoundError on teams.
📊 Production Insight
Deploys split interpreters more often than laptops do. A Dockerfile holding both a system Python and a venv resolves bare pip to whichever came first on PATH -- pin the absolute venv binary in every install line so the serving interpreter always receives the packages.
🎯 Key Takeaway
pip belongs to one interpreter; python -m pip guarantees the installer and the importer are the same binary.

Script vs -m: The Package Context That Makes Imports Work

How you launch the code rewrites the search list, which is why imports that work in the IDE die in production. Running python3 pkg/mod.py sets sys.path[0] to the pkg/ folder itself and leaves __package__ unset — the module has no idea it lives inside a package, so from pkg import helpers and any explicit relative import like from . import helpers both fail with ModuleNotFoundError. Running python3 -m pkg.mod from the project root instead sets sys.path[0] to the root directory and fills in __package__, so absolute and relative imports resolve exactly as the package author intended. IDEs hide this because they silently add the project root to the path before running, matching -m behavior without telling you. The rule for teams: pick one launch command, write it in the README, and use the identical command in systemd units, Docker CMDs, and CI steps. When a traceback shows No module named 'pkg' for your own top-level folder, don't reinstall anything — print sys.path[0], confirm it points at the script's folder instead of the root, and switch the launch to -m. Nine times out of ten that's the entire incident. A quick proof settles any debate: run the entry both ways from the project root and print sys.path[0] each time. The script form shows the package folder; the -m form shows the root. That one-line difference is the whole incident.

modulenotfound_run_context.pyPYTHON
1
2
3
4
5
6
print("__name__:", __name__)
print("__package__:", __package__)

import sys
print("entry 0:", sys.path[0])
print("run me as: python -m pkg.mod from the root")
📊 Production Insight
IDEs hide this failure by adding the project root to the path before launch. The crash appears only in production, where the raw python pkg/mod.py command runs without that help -- standardize the -m launch in the README and every service file so all environments agree.
🎯 Key Takeaway
Run package code as python -m pkg.mod from the root so sys.path[0] and __package__ agree with your imports.

A Local File Named random.py: Shadowing the Standard Library

The cruelest variant of this error isn't a missing module — it's the wrong module winning the race. Your script's own directory sits ahead of the standard library and site-packages on sys.path, so a local file named random.py, email.py, or json.py hijacks every import random in the process, including imports inside third-party libraries you didn't write. The symptoms feel haunted: random.randint suddenly doesn't exist, smtplib breaks with bizarre attribute errors, and the traceback points at your innocent utility file. New projects trip on test.py and requests.py too — the latter shadows the real HTTP library the moment someone adds it as a dependency. Diagnosis takes one line: python3 -c "import random; print(random.__file__)" shows exactly which file won. If the path points into your project, rename the shadow immediately (app_random.py, mailer.py) and purge bytecode caches, because a stale __pycache__/random.cpython-*.pyc can keep serving the shadow after the rename. Prevention belongs in CI: a lint step that fails the build when any top-level .py filename collides with the standard library list catches this before it ever ships. When the culprit file is found, check its git history too -- shadows often arrive as quick debug scripts that were never meant to be committed, and the fix includes deleting them outright.

modulenotfound_shadow.pyPYTHON
1
2
3
4
5
import random

print("random loads from:", random.__file__)
print("stdlib copy:", "site-packages" not in random.__file__)
print("randint works:", random.randint(1, 6))
⚠ Stdlib Names Are Reserved Forever
Never create top-level files named random.py, email.py, json.py, requests.py, or test.py. If one already exists, rename it now and delete its __pycache__ — the shadow outlives the rename otherwise.
📊 Production Insight
Shadow bugs survive renames through stale bytecode. Teams that rename the offending file but skip clearing __pycache__ keep crashing for another deploy -- always purge the cache folders in the same change.
🎯 Key Takeaway
Your folder outranks stdlib on sys.path, so prove every suspect import with __file__ and never reuse stdlib names.

__init__.py or Not: Regular vs Namespace Packages

Packages come in two flavors, and the missing __init__.py only breaks one of them. A regular package is a folder with an __init__.py (even an empty one) that marks the directory as importable and runs first on import — the place for package-level setup. Delete that file and older tooling, installers, and some test runners stop seeing the folder as a package, producing ModuleNotFoundError for code that sits right there on disk. The fix is trivial: restore the file. Python 3 also supports namespace packages: folders without __init__.py whose portions across several sys.path entries merge into one logical package. That design serves split distributions (plugins, large frameworks), not everyday apps — reaching for it accidentally, by deleting init files or half-installing a project, buys confusing behavior where some submodules resolve and others don't. When your own package won't import, inspect the layout directly: ls pkg/__init__.py plus python3 -c "import pkg; print(pkg.__path__)" shows whether the package resolved and which directories contributed. For application code the policy is simple: every package folder keeps its __init__.py, and namespace mechanics stay a conscious choice, not an accident.

modulenotfound_packages.pyPYTHON
1
2
3
4
5
import json

print("module file:", json.__file__)
print("is a package:", hasattr(json, "__path__"))
print("search locations:", list(json.__path__)[:2])
📊 Production Insight
Monorepo moves cause this: a folder copy drops the empty __init__.py and imports die on older runners while working locally. After any package move, run the pkg.__path__ check before merging.
🎯 Key Takeaway
Keep __init__.py in every app package; reach for namespace packages only as a deliberate, documented design.

ModuleNotFoundError vs ImportError: Subclass, Circles, Broken Code

ModuleNotFoundError is a subclass of ImportError, and the distinction decides what your except clauses mean. The child fires in exactly one situation: the import system found no module by that name anywhere on sys.path. The parent covers everything else that can go wrong around imports — a circular import where module A needs B while B is still initializing A (surfacing as ImportError: cannot import name ... from partially initialized module), a found module that raises during its own execution, or from pkg import missing_name where the package exists but the name inside it doesn't. Conflating them causes real incidents: a broad except ImportError around an optional dependency swallows a circular-import failure and reports "package not installed," sending the team to reinstall what was never missing while the true cycle hides. The discipline is narrow catching — except ModuleNotFoundError for the optional-dependency fallback, and let genuine ImportErrors crash loudly so the cycle or the broken module gets fixed. Read the message shape too: No module named 'x' (child, absent) versus cannot import name 'y' from 'x' (parent, present-but-broken) point at opposite fixes.

modulenotfound_vs_importerror.pyPYTHON
1
2
3
4
5
6
7
print("subclass:", issubclass(ModuleNotFoundError, ImportError))

try:
    import _missing_mod_abc_xyz
except ImportError as exc:
    print("caught as:", type(exc).__name__)
    print("message:", exc)
📊 Production Insight
Optional-dependency handlers cause this misdiagnosis. A broad except ImportError around an import reports a circular failure as missing, and the team reinstalls a healthy package -- keep the child exception in the fallback branch so real cycles crash loudly.
🎯 Key Takeaway
No module named means absent (child); cannot import name means present-but-broken (parent) — catch them separately.
● Production incidentPOST-MORTEMseverity: high

The Rebuild That Installed 9 Packages Into the Wrong Python

Symptom
Five minutes after a noon image rollout, all 14 web workers sat in a restart loop logging ModuleNotFoundError: No module named 'requests'. Health checks failed on every pod, the load balancer drained the fleet in 3 minutes, and checkout traffic got 500s for 18 minutes. Pip's build log showed all 9 requirements installed successfully, which made the first hour of debugging genuinely confusing.
Assumption
The team assumed pip install -r requirements.txt in the Dockerfile installed into the same interpreter that ran the app. Locally that was true — one system Python did everything. Nobody noticed the image had two: the base image's /usr/bin/python3 that owned pip, and the app venv at /opt/app/venv that ran gunicorn with 14 workers.
Root cause
The Dockerfile ran bare pip install -r requirements.txt, which resolved to the base image's /usr/bin/python3, while the gunicorn service ran /opt/app/venv/bin/python. All 9 third-party packages landed in the base interpreter's site-packages, invisible to the venv. The previous image had worked only because an engineer once installed the same list into the venv by hand during a late-night debug session — undocumented, unrepeated, and wiped by the rebuild. All 14 workers crashed within 40 seconds of rollout, serving 500s for 18 minutes until the old image was restored.
Fix
Three changes shipped in one image rebuild. First, the Dockerfile line became RUN /opt/app/venv/bin/python -m pip install -r requirements.txt, pinning installs to the serving interpreter. Second, the gunicorn unit gained ExecStart=/opt/app/venv/bin/python -m gunicorn app:server, spelling the interpreter absolutely so no PATH could swap it. Third, CI gained a smoke step running venv/bin/python -c "import requests, app" for all 9 third-party imports before the image ships. The rebuilt image deployed to all 14 workers in 6 minutes, and the import gate has caught 2 mismatches since.
Key lesson
  • Bare pip is an address, not a guarantee. It names whichever interpreter installed that script — always spell python -m pip with the interpreter you serve.
  • Smoke-test imports with the serving binary in CI. Nine one-line imports would have caught this before any worker ever restarted.
  • One image, one interpreter path. Absolute paths in Dockerfiles and unit files remove the entire class of PATH-dependent surprises.
Production debug guideFive lookup failures that cover nearly every ModuleNotFoundError page — each with the one command that names the gap.5 entries
Symptom · 01
Traceback ends with ModuleNotFoundError but pip claims the package is installed
Fix
Ask the failing interpreter to identify itself and check the package in one pass: which python3 && python3 -c "import sys; print(sys.executable); print(sys.prefix)" then python3 -m pip show <pkg>. If pip show finds it but python3 -m pip show doesn't, the package lives under a different interpreter — reinstall with python3 -m pip install <pkg>.
Symptom · 02
Imports work in your shell but crash under systemd, cron, or Docker
Fix
List every candidate on PATH: which -a python python3 pip pip3 and compare with the service file's ExecStart line. Then run python3 -c "import sys; print(sys.executable)" as the service user (with sudo -u appuser if needed). When the service interpreter differs from your shell's, activate the venv in the unit file or use its absolute /opt/app/venv/bin/python.
Symptom · 03
from pkg import x fails even though the package folder is right there
Fix
Dump the search list inside the failing context: python3 -c "import sys, pprint; pprint.pp(sys.path)" and echo $PYTHONPATH. If the project root is absent, relaunch with python3 -m pkg.mod from the root so sys.path[0] becomes the working directory, or export PYTHONPATH=/opt/app/src in the service environment.
Symptom · 04
A stdlib import like random or email suddenly lacks attributes it always had
Fix
Ask the imported module where it loaded from: python3 -c "import random; print(random.__file__)" (swap in your module name). If the path points inside your project instead of the stdlib or site-packages, list suspects with ls *.py and rename the shadow — random.py becomes app_random.py — then delete stale bytecode with find . -name '__pycache__' -type d -prune -exec rm -rf {} +.
Symptom · 05
Importing your own package fails after moving folders or deleting __init__.py
Fix
Check the package layout directly: ls pkg/__init__.py and python3 -c "import pkg; print(pkg.__path__)". If the init file is missing and imports fail on older tooling, restore an (even empty) __init__.py. If the layout is intentionally namespace-style, verify every contributor directory is installed rather than half-present.
ModuleNotFoundError Root Causes at a Glance
Root CauseHow to ConfirmFixPrevention
Package installed under a different interpreterpython3 -m pip show <pkg> prints nothing while pip show <pkg> finds it; which python3 and which pip disagreeInstall with python3 -m pip install <pkg> using the app's interpreterAlways install via python -m pip; never bare pip in docs or Dockerfiles
Wrong run context: script path vs -m from rootpython3 -c "import sys; print(sys.path[0])" shows the script's folder instead of the project rootLaunch with python3 -m pkg.mod from the root, or fix PYTHONPATHStandardize one launch command in the README and the service unit
Local file shadowing stdlib or site-packagespython3 -c "import random; print(random.__file__)" points at your project folderRename the local file and clear stale .pyc cachesLint for top-level files named like stdlib modules in CI
PYTHONPATH set locally but missing in prodecho $PYTHONPATH differs between laptop and server; sys.path dump confirms the gapRemove the dependency on PYTHONPATH; install the project properlyKeep environment diffs in versioned service files, not shell memory
Optional dependency genuinely absentpython3 -m pip show <pkg> is empty under every interpreter; exc.name names the missing top-level packageAdd it to requirements and install, or catch ModuleNotFoundError with a fallbackImport every requirements entry in CI with python -c "import pkg" after install
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
modulenotfound_sys_path.pyprint("interpreter:", sys.executable)sys.path Missed
modulenotfound_which_python.pyprint("executable:", sys.executable)pip Installed It, Python Can't See It
modulenotfound_run_context.pyprint("__name__:", __name__)Script vs -m
modulenotfound_shadow.pyprint("random loads from:", random.__file__)A Local File Named random.py
modulenotfound_packages.pyprint("module file:", json.__file__)__init__.py or Not
modulenotfound_vs_importerror.pyprint("subclass:", issubclass(ModuleNotFoundError, ImportError))ModuleNotFoundError vs ImportError

Key takeaways

1
The missing name lives in exc.name
install or fix that top-level package, not the dotted tail.
2
Always install with python -m pip; bare pip may belong to a different interpreter than your app.
3
Launch packages with python -m pkg.mod from the root so sys.path[0] and __package__ are correct.
4
A local file named like stdlib wins the import race
prove it with print(mod.__file__) and rename it.
5
Regular packages want __init__.py; namespace packages survive without one but confuse older tooling.
6
Catch ModuleNotFoundError for optional dependencies so circular imports still fail loudly as ImportError.

Common mistakes to avoid

5 patterns
×

Installing with `pip` while the app runs under a different `python3`

Symptom
pip install requests reports success, but the app crashes with ModuleNotFoundError: No module named 'requests' on the next deploy.
Fix
Install with the interpreter's own pip: python3 -m pip install requests. Verify with python3 -m pip show requests and python3 -c "import requests" before deploying.
×

Running `python3 pkg/mod.py` and relying on sibling imports

Symptom
Works in the IDE, dies in production with ModuleNotFoundError: No module named 'pkg' because sys.path[0] is the script's folder, not the project root.
Fix
Move helpers into a package and launch with python3 -m pkg.mod from the project root, or set PYTHONPATH to the root in the service unit.
×

Naming a local file `random.py` or `email.py`

Symptom
import random suddenly lacks randint, or import email breaks smtplib — the traceback points at your own file shadowing the standard library.
Fix
Rename the local file to mailer.py or app_random.py, delete the stray random.pyc, and rerun. Reserve stdlib names forever.
×

Exporting `PYTHONPATH` on your laptop and forgetting it on the server

Symptom
Imports resolve locally but crash in CI and production with the same traceback, and nobody can reproduce it on their own machine.
Fix
Keep PYTHONPATH out of production service files. Install the project (pip install -e .) so the code resolves from site-packages on every machine identically.
×

Catching broad `ImportError` to detect an optional dependency

Symptom
A circular import or a syntax-level failure inside the dependency gets misreported as "package not installed", sending the team to reinstall what was never missing.
Fix
Catch ModuleNotFoundError for the optional-dependency branch and ImportError only where circular or broken-module failures are genuinely expected.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is sys.path and in what order is it searched?
Q02JUNIOR
Why is `python3 -m pip install` safer than bare `pip install`?
Q03SENIOR
Explain the difference between `python pkg/mod.py` and `python -m pkg.mo...
Q04SENIOR
How can a local file break `import random`? How do you prove it?
Q05SENIOR
What is the subclass relationship, and why does it matter for except cla...
Q01 of 05JUNIOR

What is sys.path and in what order is it searched?

ANSWER
Python walks sys.path in order — script directory, PYTHONPATH, stdlib, site-packages — asking each finder for the top-level module. The first hit wins; no hit raises ModuleNotFoundError carrying the missing name in exc.name. sys.path[0] depends on how you launched, which is why the same import works one way and fails another.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Does pip install always target the python3 I run?
02
Why does python3 pkg/mod.py break imports that the IDE handles?
03
How do I tell which part of a dotted import is missing?
04
Can I just delete all __init__.py files?
05
Does installing my own project with pip fix local import errors?
06
Should I reinstall a package that used to import fine?
N
Naren Founder & Principal Engineer

20+ years shipping production Python across data and backend systems. Written from production experience, not tutorials.

Follow
Verified
production tested
September 23, 2026
last updated
1,905
articles · all by Naren
🔥

That's Errors. Mark it forged?

5 min read · try the examples if you haven't

Previous
Python IndexError List Index Fix
3 / 11 · Errors
Next
Python TypeError NoneType Fix