Pytest & CI

Fixing import file mismatch Errors in pytest

The error reads import file mismatch: imported module 'test_invoice' has this __file__ attribute … which is not the same as the test file we want to collect, and it usually appears the day someone adds a test file whose name already exists elsewhere in the suite. It is not a bug in pytest and not random: it is pytest noticing that two different files are trying to become the same Python module, and refusing to run the wrong one silently.

The fix is quick once the mechanism is clear, and the durable version of the fix — switching import mode — also removes the class of problem for good. This guide walks through the cause, the two fixes, and how to confirm the layout is sound afterwards.

Prerequisites

  • pytest >= 8.0; --import-mode=importlib has existed since 6.0 but is reliable for most layouts from 8.
  • Shell access to the repository to list and inspect files.
  • The layout background in test layout and import modes.

Solution

First rule out stale bytecode, then find the collision, then remove it.

Bash
# 1. Stale .pyc files from moved or renamed tests can trigger the same error.
find . -name "__pycache__" -type d -prune -exec rm -rf {} +

# 2. Which basenames occur more than once?
find tests -name "test_*.py" -printf "%f\n" | sort | uniq -d
Plain text
test_invoice.py
test_models.py
TOML
# 3a. The durable fix: import by path, no module-name collisions at all.
[tool.pytest.ini_options]
addopts = "--import-mode=importlib"
Bash
# 3b. The alternative under prepend mode: make every directory a package.
find tests -type d -exec touch {}/__init__.py \;

The second command is shown for completeness. Under the default prepend mode it works, because it changes the module names from test_invoice to tests.billing.test_invoice and tests.api.test_invoice. But it turns the test tree into an importable package called tests, which can collide with other distributions and has to be excluded from the built wheel. The configuration change avoids all of that and is the one to prefer.

How two files become one module name Two test files named test_invoice.py sit in sibling directories with no init files. Under prepend mode each directory is inserted into sys.path and each file is imported as the top-level module test_invoice. The second import finds the name already bound to the first file and pytest raises import file mismatch rather than run the wrong code. Two paths, one name, one refusal tests/billing/test_invoice.py tests/api/test_invoice.py sys.modules "test_invoice" second import: name taken by a different file pytest stops rather than silently running the first file's tests twice.
The error is protective. Without it, the second file's tests would never run and the first file's would be reported twice.

Why this works

Under the default prepend import mode, pytest walks up from each test file for as long as it finds __init__.py, stops at the first directory without one, inserts that directory at the front of sys.path, and imports the file under the dotted name formed from its path relative to that directory. Two files named test_invoice.py in directories with no __init__.py both resolve to the bare name test_invoice. The first is imported and cached in sys.modules; the second finds the name already taken by a different file, and pytest raises rather than reuse the wrong module.

--import-mode=importlib does not consult sys.path or derive names from the directory structure. It imports each file directly by location and gives it a unique name, so two files with the same basename are simply two modules. That is why it removes the error class entirely rather than working around one instance of it.

Edge cases and failure modes

  • Tests that import each other by bare name. Under importlib, from test_helpers import make_order no longer works, because the test directory is not on sys.path. Move shared helpers into a proper module under a conftest.py or an installed test-support package.
  • A partial set of __init__.py files. Adding them to some directories but not others changes where the walk stops, and can produce a new mismatch between files that previously had different names. It is all or nothing.
  • Stale .pyc after a move. A test file renamed from test_a.py to test_b.py can leave a compiled test_a behind that claims the old name. Clearing __pycache__ rules it out in a second.
  • The same basename in src and tests. A test module named like an application module can shadow it under prepend. Another reason to prefer a src layout and importlib.
  • IDE runners with their own settings. An IDE that passes its own --import-mode produces different behaviour from the command line. Put the mode in configuration so every runner inherits it.

Reading the error message precisely

The full message carries more information than its first line suggests, and reading all of it usually identifies both files without any searching.

Plain text
import file mismatch:
imported module 'test_invoice' has this __file__ attribute:
  /repo/tests/billing/test_invoice.py
which is not the same as the test file we want to collect:
  /repo/tests/api/test_invoice.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules

The first path is the file that won — the one imported earlier and cached under the bare module name. The second is the one pytest was trying to collect when it noticed. The module name in quotes is the collision itself. If the two paths are the same file at different locations — the old and new home of a moved test — the cause is stale bytecode and clearing __pycache__ is the whole fix. If they are genuinely different files, the cause is layout, and the import mode is the fix.

Triage from the two paths in the error A decision based on the two paths the error prints. If both paths refer to the same logical test at an old and a new location, stale bytecode is the cause and clearing pycache fixes it. If they are two different files with the same basename, the layout is the cause and switching import mode fixes it. The two paths tell you which fix you need same test, old and new home a file was moved or renamed and its old .pyc survived fix: clear __pycache__ nothing else changes two different files same basename in plain directories, prepend mode fix: --import-mode=importlib removes the error class
Checking which case applies takes seconds and avoids restructuring a test tree to fix what was only a stale compiled file.

CI runners that restore a cached virtual environment or workspace can carry stale bytecode between builds, which is why this error sometimes appears only in CI after a file move and never locally. Setting PYTHONDONTWRITEBYTECODE=1 in the CI environment, or excluding __pycache__ from the cache key's restored paths, removes that source permanently.

Choosing between the two fixes

Both fixes make the error go away, and they differ in what they leave behind.

The __init__.py route keeps the default import mode, which some older plugins and some cross-test imports depend on. Its cost is structural: the test tree becomes a package, every new directory must remember to include the marker file, and the package name tests is claimed in the import namespace. Forgetting one __init__.py in a new directory is enough to reintroduce the error months later, typically for whoever adds the next duplicate basename.

The importlib route changes one configuration line and removes the marker files. Its cost is behavioural: test modules can no longer import one another by bare name, and a very small number of plugins that assume prepend semantics need updating. For a new or medium-sized suite that cost is nearly zero; for a large legacy suite it is a few hours of moving shared helpers into proper modules, which is an improvement in its own right.

Comparing the two fixes Two columns. Adding init files keeps prepend mode and cross-test imports but turns the test tree into a package that every new directory must maintain. Switching to importlib mode removes the marker files and the collision class entirely, at the cost of test modules no longer importing each other by bare name. Both fix today's error; only one prevents the next add __init__.py everywhere ✓ keeps prepend semantics ✓ cross-test imports still work ✗ tree becomes package "tests" ✗ every new dir must remember the error returns when one is missed --import-mode=importlib ✓ no module-name collisions ✓ no marker files to maintain ✓ tests stay out of the namespace ✗ no bare-name cross-test imports the error class is gone for good
The right-hand column's one cost is also a small improvement: shared helpers end up in a real module rather than in another test file.

Confirming the layout is sound

After either fix, two checks confirm that the suite now collects the same tests however it is invoked.

Run collection from the repository root and from inside a test subdirectory, and compare the results. With testpaths and the import mode in configuration, they should be identical; if they differ, some part of the behaviour still depends on the current directory, and the next person to run a single file from their editor will see a different suite from CI.

Then add a new test file whose basename duplicates an existing one, run the suite, and delete it again. Under importlib it should simply collect. This takes thirty seconds and proves the fix addresses the class of problem rather than the instance that prompted it — which matters because the next duplicate basename will be added by someone who has never heard of this error and would otherwise spend an hour rediscovering it. Recording the chosen import mode and the reason in a comment beside the configuration completes the job, so that nobody later removes it as an unexplained setting.

Frequently Asked Questions

Why does the error mention pycache? Because a compiled file from a previous location can also trigger the check. If a test file was moved or renamed, a stale .pyc may still claim the old module name. Deleting __pycache__ directories, or running with -p no:cacheprovider once, rules this out before investigating the layout.

Is adding init.py everywhere a real fix? It is a working fix under the default prepend mode, because it makes module names unique by turning the test tree into packages. The cleaner fix is --import-mode=importlib, which removes the need for those files entirely and avoids the test tree becoming an importable package called tests.

Can two test files ever safely share a basename? Yes, under importlib mode, or under prepend mode when both live inside packages so their dotted names differ. What cannot work is two same-named files in plain directories under prepend mode, because both become the top-level module test_name.

← Back to Test Layout & Import Modes