Python

Python ModuleNotFoundError When Running Script From a Parent Folder

· by DebuggedIt

Quick answer

A script that imports fine when run from one directory but fails with ModuleNotFoundError when run from a different directory (particularly a parent folder)...

A script that imports fine when run from one directory but fails with ModuleNotFoundError when run from a different directory (particularly a parent folder) comes down to how Python determines its module search path — which is based on the current working directory and the script's own location, not a fixed, absolute understanding of your project's structure.

The Problem

Running a script from its own directory works fine, but running the exact same script from a parent directory fails:

cd project/scripts
python my_script.py  # works fine

cd project
python scripts/my_script.py  # ModuleNotFoundError: No module named 'utils'

The imported module clearly exists in the project, which makes the directory-dependent failure confusing without understanding exactly how Python resolves import paths.

Where Python looks for modules 1. Directory of the script being run (sys.path[0]) 2. PYTHONPATH env variable (if set) - often NOT set

Why It Happens

When you run a Python script directly (python my_script.py), Python automatically adds the directory containing that script to sys.path — but it adds the script's own directory, not the directory you happened to run the command from, and not automatically any other directory in your project structure. This means a module import that works when Python's automatic path addition happens to include the right directory can fail once you run the same script from a different location, since the automatically-added path changes based on the script's own location relative to where it's invoked, and other project directories aren't automatically included at all.

The Fix

The most robust, portable fix is running your script as part of a proper Python package, using the -m flag, rather than invoking the file directly by path — this uses Python's package import system correctly, resolving imports relative to your project's actual package structure rather than the script's own file location:

# From the project root, with an __init__.py making 'scripts' a proper package
python -m scripts.my_script

This requires your project to have proper package structure (an __init__.py file in each directory that should be treated as a package), but is the generally recommended, most robust long-term approach for any project with imports spanning multiple directories:

project/
├── scripts/
│   ├── __init__.py
│   └── my_script.py
├── utils/
│   ├── __init__.py
│   └── helpers.py

With this structure, running python -m scripts.my_script from the project root correctly resolves from utils import helpers-style imports regardless of exactly where you're invoking the command from, since -m handles the module resolution based on your project's package structure rather than file system location relative to the current working directory.

If restructuring into a proper package isn't immediately practical, explicitly add the necessary directory to sys.path at the top of your script, before the problematic import — a common, pragmatic workaround, though less elegant than proper package structure:

import sys
import os

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

from utils import helpers

os.path.dirname(__file__) reliably gives you the script's own directory regardless of where it was invoked from, and navigating relative to that (rather than relative to the current working directory, which varies based on where you run the command) makes this path addition consistent regardless of your invocation location.

For a project-wide, more permanent solution, set the PYTHONPATH environment variable to include your project's root directory, ensuring Python's import resolution always includes it regardless of which specific script you're running or from where:

export PYTHONPATH="${PYTHONPATH}:/path/to/project"
python scripts/my_script.py

For projects managed with a proper packaging setup (a pyproject.toml or setup.py), installing your project in editable/development mode makes its modules importable from anywhere, completely sidestepping directory-relative import concerns entirely:

pip install -e . --break-system-packages

Still Not Working?

If you've adopted the -m flag approach but still see import errors, confirm every intermediate directory in the import chain actually has an __init__.py file — a missing __init__.py in any directory along the path (even one that seems like it should be treated as a package based on its contents) prevents Python from recognizing it as a proper package, producing exactly this kind of import failure even when the overall directory structure otherwise looks correct:

find . -type d -exec test -f "{}/__init__.py" \; -o -print
# lists directories missing an __init__.py, potential culprits for import failures