The module is probably installed — just not for the interpreter you're running, or not on sys.path. Trace which python is executing and where it looks, and the error resolves fast.
pip and python can point to different installations. python -m pip install <pkg> guarantees the install goes to the interpreter you run.
Running python app/main.py from inside app/ means the parent dir isn't importable. Print the search path: python -c "import sys; print(sys.path)".
A local file named like the package (email.py, json.py, logging.py) shadows the stdlib/installed module. The import silently loads yours and explodes elsewhere.
which python python3 && python -c "import sys; print(sys.executable)"
python -m pip install <pkg> # and in the right env: source ~/.venvs/proj/bin/activate first
python -c "import <pkg>; print(<pkg>.__version__, <pkg>.__file__)"
# cd <repo-root> && python -m package.main — or: pip install -e . so your own code is importable anywhere
cron/systemd jobs run with different PATH and no activated venv — point them at the venv's absolute python path. docker: installing with a different user or --user flag than the runtime user produces exactly this error.
Two Pythons. The pip you ran belongs to another interpreter or env. Use python -m pip (same interpreter as the failing script) and check sys.executable to confirm.
Those environments don't activate your venv and start with a minimal PATH. Use absolute paths: /home/you/.venvs/proj/bin/python script.py in the service/cron definition.
Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.
Browse the template store →One-time. Yours to modify. Instant download from the NinjaOps template store.