What this usually means
The most common cause is that the shell or IDE is not actually using the virtual environment's Python interpreter. When you `activate` a venv, it modifies `$PATH` to prepend the venv's `bin` directory. If something overrides that (e.g., a hardcoded shebang, a conflicting alias, a .bashrc that resets PATH), you end up running the system Python. Another subtle cause: installing packages with `pip` while the venv is active but then running a script that was started before activation, or using `sudo pip` which installs globally. Also, some IDEs (VS Code, PyCharm) have their own venv selection that must be explicitly set.
The first ten minutes — establish facts before touching code.
- 1Run `which python` and `which pip` — both must point inside the venv (e.g., `/path/to/venv/bin/python`).
- 2Run `python -c "import sys; print(sys.executable)"` — verify it's the venv python.
- 3Run `pip list` and confirm the package is listed; if not, run `pip install <package>`.
- 4Check `pip --version` — it should show the venv path, not a global one.
- 5If using VS Code, check the Python interpreter at the bottom-left of the window; it must point to the venv.
The specific files, logs, configs, and dashboards that usually own this bug.
- search`~/.bashrc`, `~/.zshrc`, or `~/.profile` for PATH overrides or `alias python=...`.
- searchThe virtual environment's `pyvenv.cfg` file (check `home` key points to correct Python installation).
- search`/path/to/venv/bin/python` — ensure it exists and is executable.
- search`/path/to/venv/lib/pythonX.Y/site-packages/` — verify the package is physically there.
- searchIDE settings: VS Code `.vscode/settings.json`, PyCharm Project Interpreter.
- search`pip list --format=columns` vs `pip list --user` to see if package is installed user-site.
- search`python -m site` to see site-packages paths; look for the venv path.
Practical causes, not theory. These are the things you will actually find.
- warningForgot to activate the virtual environment (`source venv/bin/activate`).
- warningActivated the venv but the terminal is a subshell or a new terminal tab that wasn't activated.
- warningInstalled the package globally with `sudo pip` while the venv was active (sudo drops environment).
- warningRunning a script with a shebang `#!/usr/bin/python` that bypasses the venv.
- warningIDE running its own terminal without inheriting the activation (e.g., VS Code's integrated terminal uses the default shell, not the selected interpreter).
- warningVirtual environment created with `--system-site-packages` and a conflicting global package is shadowing the venv one.
- warningPATH is being reset by a tool like `direnv`, `virtualenvwrapper`, or `conda` after activation.
Concrete fix directions. Pick the one that matches your root cause.
- buildExplicitly use the venv Python: `./venv/bin/python script.py` instead of relying on `python`.
- buildRe-create the venv: `rm -rf venv && python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt`.
- buildUpdate PATH in shell config to remove any `python` aliases or conflicting paths.
- buildIn VS Code, set `"python.defaultInterpreterPath": "./venv/bin/python"` in settings.json.
- buildUse `pip install --force-reinstall <package>` to ensure the package is installed in the correct venv.
- buildIf using a Docker container, ensure the venv is sourced in the Dockerfile's `CMD` or entrypoint.
A fix you cannot prove is a guess. Close the loop.
- verifiedRun `which python` and confirm it's the venv path.
- verifiedRun `python -c "import sys; print(sys.path)"` — the first entry should be the venv's site-packages.
- verifiedRun `python -c "import <package>; print(<package>.__version__)"` — should succeed and print version.
- verifiedRe-run the original failing script — it should now succeed.
- verifiedOpen a new terminal, activate the venv, and run the same script to confirm persistence.
- verifiedCheck the shebang of the script: `head -1 script.py` — should be `#!/usr/bin/env python` or `#!/path/to/venv/bin/python`.
Things that make this bug worse or harder to find.
- warningUsing `sudo pip install` inside a venv — it installs globally.
- warningEditing system Python's site-packages directly; always use a fresh venv.
- warningAssuming activation persists across terminal sessions; always activate per session.
- warningRunning `pip install` with a different Python version than the venv's Python (e.g., `pip3` vs `python -m pip`).
- warningIgnoring the IDE's Python interpreter selection; it's separate from the terminal's activation.
- warningUsing `--user` flag inside a venv — it installs to the user site, not the venv.
The Case of the Disappearing Requests Library
Timeline
- 09:15Created venv: `python3 -m venv venv` and activated it.
- 09:17Installed Flask and requests: `pip install flask requests`.
- 09:20Wrote `app.py` with `import requests` and `from flask import Flask`.
- 09:25Ran `python app.py` — got `ModuleNotFoundError: No module named 'requests'`.
- 09:27Checked `pip list` — requests was listed. Confused.
- 09:30Ran `which python` — returned `/usr/bin/python3` instead of venv path.
- 09:32Realized the VS Code integrated terminal wasn't sourcing the activation script.
- 09:35Manually ran `source venv/bin/activate` again; `which python` now correct.
- 09:36Re-ran `python app.py` — it worked.
I was building a simple Flask API that needed the `requests` library to call an external service. I followed the standard routine: created a virtual environment, activated it, installed dependencies, and started coding. When I hit run in VS Code's integrated terminal, Python immediately complained that `requests` was missing. I double-checked `pip list` right after — there it was, `requests 2.28.1`. How could Python see the package in pip but not import it?
I spent ten minutes staring at the code, re-installing packages, even removing and re-creating the venv. Nothing worked. Then I remembered a previous bug: `which python` showed `/usr/bin/python3`. The terminal wasn't using the venv's Python at all. The activation had somehow been lost — likely because VS Code's integrated terminal doesn't automatically source the activation script when it opens a new shell. The `(venv)` prefix in my prompt was misleading; it was just a leftover from a previous activation.
I manually ran `source venv/bin/activate` again, confirmed `which python` now pointed to the venv, and re-ran the script. It worked immediately. The root cause was a mismatch between the visible prompt and the actual PATH. The lesson: never trust the prompt; always verify with `which python` and `python -c 'import sys; print(sys.executable)'`.
Root cause
The VS Code integrated terminal did not automatically source the virtual environment activation script, so the shell was using the system Python even though the prompt showed `(venv)`.
The fix
Manually run `source venv/bin/activate` in the terminal, or configure VS Code to automatically activate the venv by setting `"python.terminal.activateEnvironment": true` in settings.json.
The lesson
Always verify the Python interpreter path explicitly (which python) before debugging import errors. The terminal prompt can be misleading.
When you run `source venv/bin/activate`, the shell executes a script that does two critical things: it prepends the venv's `bin` directory to the `PATH` environment variable, and it sets the `VIRTUAL_ENV` environment variable to the venv's path. The modified `PATH` means that when you type `python`, the shell finds the venv's Python first. The `VIRTUAL_ENV` variable is used by some tools (like `pip`) to detect the active venv.
The activation script also changes the shell prompt to show `(venv)` — but this is purely cosmetic. If something resets `PATH` after activation (e.g., a `.bashrc` line that sets `PATH=/usr/local/bin:$PATH`), the venv bin is lost, but the prompt remains. That's the trap: the prompt lies. Always use `which python` or `echo $VIRTUAL_ENV` to check real activation status.
A common misconception is that `pip install` inside an activated venv always installs to that venv. However, if you use `sudo pip install`, `sudo` resets environment variables (including `PATH` and `VIRTUAL_ENV`) for security reasons, so the package ends up in the system site-packages. Similarly, `pip install --user` installs to `~/.local/lib/pythonX.Y/site-packages`, not the venv.
The safest command is always `python -m pip install <package>` because it uses the `python` that is currently on your PATH (which you've verified is the venv one). This avoids any ambiguity about which pip executable is being used.
When you get an import error, `sys.path` tells you exactly where Python is looking for modules. Run `python -c "import sys; print(sys.path)"` inside the activated venv. The first entry should be the empty string (current directory), followed by the venv's site-packages. If you see system paths like `/usr/lib/python3.9` before the venv path, Python is using the system Python libraries.
This often happens when the venv was created with `--system-site-packages`, which adds the system site-packages to the path. While sometimes useful, it can cause shadowing: a global package with the same name as a venv one will be imported first. In that case, check the order of paths and consider recreating the venv without that flag.
If your system has multiple Python versions (e.g., 3.8, 3.9, 3.10), you might create a venv with one version but later use another. For example, `python3 -m venv venv` uses the default `python3` (which might be 3.8), but then you run `python3.9 app.py` — which is outside the venv. Always use the same Python executable to create the venv and run scripts.
A reliable pattern is to use the full path: `/usr/bin/python3.9 -m venv venv` and then `venv/bin/python app.py`. This guarantees version consistency. You can also check the `pyvenv.cfg` file in the venv directory; the `home` key shows the Python installation used to create it.
Frequently asked questions
Why does `pip list` show the package but `import` fails?
This happens when the `pip` you ran is from the venv, but the `python` you use to run the script is the system Python. They are different interpreters. The package is installed in the venv's site-packages, but the system Python doesn't look there. Always verify `which python` and `which pip` point to the same venv.
How do I know if my virtual environment is activated?
Run `which python` — it should show a path inside your venv (e.g., `/home/user/myproject/venv/bin/python`). Also run `echo $VIRTUAL_ENV` — it should print the venv directory. The prompt `(venv)` is not reliable.
Should I use `pip` or `python -m pip`?
Always use `python -m pip` inside a venv. This ensures you're using the pip associated with the venv's Python. Plain `pip` might resolve to a different pip if PATH is misconfigured.
Why does my IDE say the package is not found even after installing it in the terminal?
Your IDE likely has its own Python interpreter selection that is different from the terminal's venv. In VS Code, use 'Python: Select Interpreter' and choose the venv's Python. In PyCharm, go to Settings > Project > Python Interpreter and add the venv.
What is the `--system-site-packages` flag and should I use it?
The `--system-site-packages` flag allows the venv to access the system's site-packages. This can be convenient but often leads to version conflicts. It's better to avoid it and install all dependencies inside the venv. If you already have it, check `sys.path` order to see if system packages shadow your venv packages.