ImportError: attempted relative import with no known parent package
1 · The lesson
readWhat this error means
The file has an import with a leading dot — from .totals import invoice_total — and Python doesn't know what package that dot refers to. A relative import means "from the package I belong to". When you run a file directly with python path/to/file.py, Python loads it as a standalone script called __main__, with no package at all. So "relative to my package" has nothing to be relative to.
The code is usually fine. The way you started it is the problem.
When you see it
Take a small project with one package:
billing/
invoices/
__init__.py
totals.py
report.pyreport.py imports from its sibling:
# invoices/report.py from .totals import invoice_total
Run that file directly — from the project root or from inside invoices/, it makes no difference:
cd C:\Users\Ada\billing python invoices/report.py
Traceback (most recent call last): File "C:\Users\Ada\billing\invoices\report.py", line 1, in <module> from .totals import invoice_total ImportError: attempted relative import with no known parent package
python -m report from inside the package folder fails the same way, with two extra <frozen runpy> lines at the top of the traceback.
Why it happens
Every module knows two things about itself: its name (__name__) and the package it lives in (__package__). Relative imports are resolved from __package__. Here is what each way of starting report.py gives it:
| How you run it | __name__ | __package__ | from .totals works? |
|---|---|---|---|
python invoices/report.py | '__main__' | None | No |
cd invoices then python report.py | '__main__' | None | No |
python -m invoices.report (from billing/) | '__main__' | 'invoices' | Yes |
With None as the package, the leading dot has no meaning, and Python raises this error rather than guess.
Note what does not fix it: adding an __init__.py. The package above already has one. __init__.py marks a folder as a package, but only for code that is imported as part of that package — running a file by its path skips packages entirely.
How to fix it
Run it as a module, from the folder above the package. This is the real fix, and it keeps the relative import:
cd C:\Users\Ada\billing python -m invoices.report
Invoice total: 11.25Two details trip people up. Use dots, not slashes, and leave off .py — it is invoices.report, a module name, not a file path. And run it from billing/, the folder that contains invoices/, so Python can find the package.
Or switch to an absolute import. from invoices.totals import invoice_total names the package outright. It still needs python -m invoices.report from the project root — run the file directly and you trade this error for a different one:
Traceback (most recent call last): File "C:\Users\Ada\billing\invoices\report.py", line 1, in <module> from invoices.totals import invoice_total ModuleNotFoundError: No module named 'invoices'
Absolute imports are the style PEP 8 recommends, and they read better in a large codebase, but they do not remove the need to start the program properly.
Better: keep entry points outside the package. A file you run directly shouldn't be one that other modules import. Put a small script next to the package and let everything inside use relative imports freely:
billing/
run_report.py <- python run_report.py
invoices/
__init__.py
totals.py
report.py# run_report.py from invoices.report import main main()
Now report.py defines a main() function instead of doing its work at the top level, python run_report.py works from billing/, and so do your tests. For an installable project, the same idea is a [project.scripts] entry in pyproject.toml.
Don't drop the dot to get from totals import .... The file then runs by path, but the moment it is used as part of the package — python -m invoices.report, or an import from anywhere else — it fails with No module named 'totals'. And don't reach for sys.path.append(...) at the top of the file: it makes imports depend on where the file sits on disk, and the next person to move a folder inherits the mess.
In your editor, change how it runs the file. The Run button is usually what causes this:
- VS Code: "Run Python File" runs
python path/to/file.py. Create a debug configuration in.vscode/launch.jsonwith"module": "invoices.report"instead of"program", and set"cwd"to the project root. - PyCharm: edit the run configuration and switch the dropdown from Script path to Module name, then enter
invoices.report. Set the working directory to the project root.
You can watch Python do the resolving with importlib.util.resolve_name, the same rule the import statement uses. Give it a package and the dot turns into a real module name; give it none and you get this page's error in different words:
from importlib.util import resolve_name print(resolve_name(".totals", "invoices")) # python -m invoices.report print(resolve_name("..config", "billing.invoices")) # two dots climb one level try: resolve_name(".totals", None) # python invoices/report.py except ImportError as err: print("ImportError:", err)
When you'd actually see this in real code
- Clicking Run in VS Code on a module deep inside
src/myapp/to test one function. - A Flask or FastAPI project where
app/main.pydoesfrom .routes import routerand someone runspython app/main.pyinstead ofuvicorn app.main:apporpython -m app.main. - Adding
if __name__ == "__main__":test code to a module that uses relative imports, then trying to run it. - Reorganising a flat folder of scripts into a package — the scripts were run by path before, and still are.
Related errors
- ModuleNotFoundError: No module named 'X' — what you get after "fixing" this by switching to an absolute import and still running the file by path.
- ImportError: cannot import name 'X' from 'Y' — the module was found, the name inside it wasn't.
- ImportError: cannot import name 'X' from partially initialized module — two modules importing each other.
ImportError: attempted relative import beyond top-level package— you ran it as a module, but..climbed above the outermost package Python knows about. Run from one folder higher, naming the full dotted path.
See Also
- All Python errors — the full index, by type and by when it happens.
- Modules & Imports
- Packaging
- The terminal
Found this useful? Share it with a friend who’s learning Python.