2025, Oct 04 21:00
Resolve mypy 'Source file found twice under different module names' by aligning imports and package discovery
Understand why mypy reports 'Source file found twice under different module names' and learn three fixes: adjust imports, run mypy -p, or add __init__.py.
When mypy reports “Source file found twice under different module names,” it means the same Python file is being indexed under two import paths. This often happens in projects where a directory like testing isn’t a real package, but code is imported as if it were. The result is duplicate module identities and a hard stop from the type checker.
Minimal example that reproduces the issue
The scenario below mirrors the shape of the problem. The testing directory is searched recursively, it contains a package subtree, but testing itself is not a package.
mkdir -p testing/pkgcore
touch testing/pkgcore/{__init__,modutil}.py  # testing/__init__.py is intentionally absent
echo "from testing.pkgcore import modutil" > testing/test_modutil.py
mypy testing
mypy responds with the duplicate source error:
testing/pkgcore/modutil.py: error: Source file found twice under different module names: "modutil" and "testing.pkgcore.modutil"
testing/pkgcore/modutil.py: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#mapping-file-paths-to-modules for more info
testing/pkgcore/modutil.py: note: Common resolutions include: a) adding `__init__.py` somewhere, b) using `--explicit-package-bases` or adjusting MYPYPATH
Found 1 error in 1 file (errors prevented further checking)
What’s actually going on
Running mypy testing asks mypy to crawl the testing folder and infer packages from the presence of __init__.py files. The first directory it encounters that has an __init__.py is treated as a root package. Everything underneath is named relative to that root. In our example, testing/pkgcore has __init__.py, so mypy maps testing/pkgcore/modutil.py to the module name pkgcore.modutil.
The problem is that the import in test_modutil.py uses from testing.pkgcore import modutil. That statement asserts that testing is itself a package, which contradicts how mypy discovered the module. mypy then sees the same file twice: once as pkgcore.modutil and once as testing.pkgcore.modutil. That mismatch triggers the duplicate source error.
The same pattern showed up in a real-world case: testing/example_scripts/rewrite/tests/test_main.py imported from testing.example_scripts.rewrite.src.main import func, while mypy’s discovery treated testing/example_scripts/rewrite/src/main.py as belonging to a different root, so the file appeared under two identities.
How to fix it
There are three straightforward ways to bring the import paths and mypy’s module discovery into agreement. Choose one that matches your project’s layout and tooling expectations.
First, align the imports with how mypy names the discovered module. If mypy treats testing/pkgcore/modutil.py as pkgcore.modutil, importing through testing is what causes the duplication. Switching the import to the discovered name resolves the conflict.
echo "from pkgcore import modutil" > testing/test_modutil.py
mypy testing
# Success: no issues found in 3 source files
This option emphasizes that testing is part of a source layout, not a package. It keeps tests stored separately while importing code by its actual package name. Other tools may need a sys.path tweak so that testing is on the module search path during their runs.
Second, call mypy as a package-aware check. Using the -p flag tells mypy explicitly that testing is a package namespace. With that hint, mypy maps testing/pkgcore/modutil.py to testing.pkgcore.modutil, which then matches your imports.
mypy -p testing
# Success: no issues found in 4 source files
Third, make testing a package by adding an __init__.py at its root. Once testing is a package, mypy will assign module names starting from testing, again matching imports like from testing.pkgcore import modutil.
touch testing/__init__.py
mypy testing
# Success: no issues found in 4 source files
Why this matters
Consistent module identity is foundational for static analysis. If the same file can be reached via different import paths, type checkers see two unrelated modules with identical content. That breaks incremental analysis, invalidates caches, and can mask genuine errors behind a flood of duplicate-source failures. Beyond mypy, the same inconsistency can bleed into runtime by producing surprising import behaviors in tests and tools.
Takeaways
When running mypy over a directory tree, understand how it maps file paths to module names. Either import code using the names mypy infers, invoke mypy in a way that declares your package boundaries, or add the missing __init__.py to formalize the package. If you hit the duplicate-source message, the mypy output points to helpful directions, including the documentation on path-to-module mapping and hints like adding __init__.py, using --explicit-package-bases, or adjusting MYPYPATH. Keeping your import graph aligned with your package layout avoids these conflicts and makes type checking predictable.