Skip to content

How to Fix "ImportError: attempted relative import with no known parent package"

ImportError: attempted relative import with no known parent package means Python ran a file that uses a relative import, but that file had no parent package to resolve the import against. It almost always comes from launching the file the wrong way, not from a mistake in the import itself.

Quick fix: from the project root, run the file as a module instead of a script. Use python -m mypackage.app (dotted path, no .py), not python mypackage/app.py. The rest of this guide explains why, offers an alternative, and shows how to make the fix permanent with an entry point.

Reproduce the error

Create a small package with two files. helpers.py holds a function, and app.py imports it with a relative import:

      • __init__.py
      • helpers.py
      • app.py
mypackage/app.py
from .helpers import greeting


def main():
    print(greeting())


if __name__ == "__main__":
    main()

Run the file directly, the way you would run any script, and it fails:

$ python mypackage/app.py
Traceback (most recent call last):
  File "mypackage/app.py", line 1, in <module>
    from .helpers import greeting
ImportError: attempted relative import with no known parent package

The from .helpers line is correct. The problem is how the file was launched. An editor’s Run button does the same thing: VS Code’s “Run Python File” and PyCharm’s run arrow both execute the current file by its path, so they raise this error too.

Why does running a file directly break relative imports?

When you run python mypackage/app.py, Python loads app.py as the top-level script and sets its __package__ to None. A relative import like from .helpers needs __package__ to name a parent package, and None gives it nothing to resolve against. See what a Python module is for how Python assigns __name__ and __package__.

Run the module with python -m

Run the file as a module instead of a script. From the project root (the directory that contains mypackage/), use the -m flag with the dotted import path and no .py extension:

$ python -m mypackage.app
hello from helpers

python -m mypackage.app imports app as a submodule of mypackage, so Python sets __package__ to "mypackage" and the relative import resolves. Point -m at the specific module (mypackage.app), not the package (mypackage). Running python -m mypackage requires a __main__.py inside the package and fails without one.

The folder does not strictly need an __init__.py for this to work. Python treats a directory without one as a namespace package, so python -m mypackage.app runs either way. Add an empty __init__.py when you want an explicit package, which most tools and the entry-point setup below expect.

Run the file directly with an absolute import

To keep launching the file as a script, change its relative import to an absolute one:

mypackage/app.py
from helpers import greeting

Running python mypackage/app.py puts the file’s own directory on Python’s import path, so the sibling helpers module resolves by its bare name. This works only while you launch from inside the package directory. The moment the same code is imported as part of a package, import helpers fails because helpers is no longer a top-level module. For anything you package or share, prefer python -m or an entry point.

Give the program a permanent entry point

Typing python -m mypackage.app works, but it forces you and your users to know the internal module path and to stand in the right directory. A published entry point removes both requirements. Declare one in pyproject.toml under [project.scripts], pointing at the module and function to call:

pyproject.toml
[project.scripts]
greet = "mypackage.app:main"

The value "mypackage.app:main" names the module (mypackage.app) and the function (main). Once the package is installed, running greet calls main() through the package, so __package__ is set and the relative import resolves.

uv scaffolds this layout for you. uv init --package creates a project with a [project.scripts] entry point already wired to a starter main():

$ uv init --package --name mytool myproject
$ cd myproject
$ uv run mytool
Hello from mytool!

The scaffold writes mytool = "mytool:main", pointing at a main() in src/mytool/__init__.py. Replace that placeholder with your own code. uv run installs the package into the project’s virtual environment as an editable install, then runs the console script. Because the code runs through the installed package, __package__ is always set and relative imports resolve from any directory.

Structure imports in a src-layout project

uv init --package uses the src layout, which places the package under a src/ directory:

    • pyproject.toml
        • __init__.py
        • helpers.py

src/ is not on Python’s import path, so the package is only importable after uv sync installs it. That installed package is what makes relative imports inside mytool resolve. Modules within the package import each other with relative imports (from .helpers import greeting) or with the package name (from mytool.helpers import greeting); both work once the package is installed.

Running a file by its path still fails, even in a src layout:

$ uv run python src/mytool/__init__.py
ImportError: attempted relative import with no known parent package

The rule holds regardless of layout: for code you package or share, reach it through the package with an entry point or python -m, rather than by file path. That is the launch method that keeps working as the project grows.

Learn More

Last updated on