Skip to content
Latchkey

Python "shutil.which() returned None" - Missing External Tool in CI

shutil.which("tool") returned None because the external binary your code shells out to isn’t on PATH in CI. The Python code then crashes when it tries to use the missing path - often as a confusing TypeError or a downstream FileNotFoundError.

What this error means

A script that locates a helper binary with shutil.which gets None, then fails - either a RuntimeError you raise, or a TypeError: expected str ... not NoneType when the None is passed to subprocess. It works locally where the tool is installed.

Python traceback
tool = shutil.which("ffmpeg")   # -> None in CI
subprocess.run([tool, "-i", src])
# TypeError: expected str, bytes or os.PathLike object, not NoneType
# or your own:
RuntimeError: required executable 'ffmpeg' not found on PATH

Common causes

External tool not installed on the runner

The binary (ffmpeg, graphviz/dot, git, pandoc) is present on dev machines but not in the minimal CI image, so which finds nothing.

Installed but not on PATH

The tool exists at a non-standard location not in PATH (e.g. installed to /opt/... without a symlink), so shutil.which still returns None.

How to fix it

Install the external tool in CI

Terminal
# Debian/Ubuntu - example: ffmpeg
apt-get update && apt-get install -y ffmpeg
python -c "import shutil; print(shutil.which('ffmpeg'))"

Add the tool’s directory to PATH

If it’s installed but not discoverable, put its bin directory on PATH for the job.

Terminal
export PATH="/opt/ffmpeg/bin:$PATH"
# GitHub Actions, persist across steps:
echo "/opt/ffmpeg/bin" >> "$GITHUB_PATH"

Fail with a clear message

Guard the lookup so a missing tool produces an actionable error instead of a cryptic NoneType crash.

Python
tool = shutil.which("ffmpeg")
if tool is None:
    raise RuntimeError("ffmpeg not found on PATH; install it in CI")

How to prevent it

  • Bake required external binaries into the runner image.
  • Validate tool presence early with a clear error, not a NoneType crash.
  • Document the OS-level tools your scripts shell out to.

Related guides

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