pytest-dev/pytest · error · ValueError

found 2 or more testreports matching

Error message

found 2 or more testreports matching {inamepart!r}: {values}

What it means

Thrown by Pytester.matchreport() when more than one captured report matches the given inamepart filter. The method is designed to return a single unambiguous report; multiple matches mean the filter is too broad.

Solutions

  1. Provide a more specific inamepart that uniquely identifies one nodeid segment (include the parametrize id, e.g. 'test_foo[0]').
  2. Use getreports() and filter the list yourself when you genuinely want multiple.
  3. Reduce the test suite being run through pytester so only one matching test exists.

Example fix

// before
rep = reprec.matchreport("test_foo")
// after - disambiguate by full parametrize id
rep = reprec.matchreport("test_foo[1-2]")
Defensive patterns

Strategy: validation

Validate before calling

matches = [r for r in reprec.getreports() if inamepart in r.nodeid.split('::')]
if len(matches) > 1:
    # narrow the filter or use the list directly
    nodeids = [r.nodeid for r in matches]
    raise AssertionError(f"ambiguous: {nodeids}")

Prevention

When it happens

Trigger: Calling matchreport('test_foo') when several parametrized cases share that node-id prefix (e.g. test_foo[0], test_foo[1]); passing an empty inamepart when multiple tests ran; matching on a class name that contains multiple test methods.

Common situations: Parametrized tests where each callspec produces a separate TestReport; running multiple test functions and passing an empty filter expecting one; refactoring that merged test names.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/8bc888de769557cb. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/pytester.py:379

        when: str | None = None,
    ) -> CollectReport | TestReport:
        """Return a testreport whose dotted import path matches."""
        values = []
        for rep in self.getreports(names=names):
            if not when and rep.when != "call" and rep.passed:
                # setup/teardown passing reports - let's ignore those
                continue
            if when and rep.when != when:
                continue
            if not inamepart or inamepart in rep.nodeid.split("::"):
                values.append(rep)
        if not values:
            raise ValueError(
                f"could not find test report matching {inamepart!r}: "
                "no test reports at all!"
            )
        if len(values) > 1:
            raise ValueError(
                f"found 2 or more testreports matching {inamepart!r}: {values}"
            )
        return values[0]

    @overload
    def getfailures(
        self,
        names: Literal["pytest_collectreport"],
    ) -> Sequence[CollectReport]: ...

    @overload
    def getfailures(
        self,
        names: Literal["pytest_runtest_logreport"],
    ) -> Sequence[TestReport]: ...

    @overload
    def getfailures(

View on GitHub (pinned to 0d6fbdeffa)