Skip to content
Latchkey

Python venv Not Activated - Packages Missing Across CI Steps

You installed packages into a virtualenv in one step, but a later step runs in a fresh shell that never activated it - so the interpreter on PATH is the system Python with none of your packages.

What this error means

A package installs fine, yet a later step fails with ModuleNotFoundError for that exact package. Activating the venv inside that step makes it work, proving the activation did not carry over.

CI log
Step "Install": Successfully installed flask-3.0.0
Step "Run": ModuleNotFoundError: No module named 'flask'

Common causes

Each CI step is a new shell

In GitHub Actions and most CI systems, every run: step starts a fresh shell. A source .venv/bin/activate in one step does not persist to the next.

PATH/VIRTUAL_ENV not exported between steps

Activation just edits PATH for the current shell. Without exporting it to the job environment, later steps fall back to the system interpreter.

How to fix it

Add the venv to the job PATH once

On GitHub Actions, write the venv bin directory to $GITHUB_PATH so every later step uses it.

.github/workflows/ci.yml
- run: |
    python -m venv .venv
    echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
- run: pip install -r requirements.txt   # uses the venv
- run: pytest                            # also uses the venv

Or activate within each step

If you prefer, activate at the start of every step that needs the venv.

.github/workflows/ci.yml
- run: |
    . .venv/bin/activate
    pytest

How to prevent it

  • Persist the venv bin to the job PATH instead of relying on per-step activation.
  • Cache the venv keyed on your lockfile to keep steps consistent.
  • Standardize on python -m invocations that target the active interpreter.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →