pytest-dev/pytest · error · TypeError

ids must be a callable or an iterable

Error message

ids must be a callable or an iterable

What it means

Thrown by Metafunc._validate_ids() when the `ids` argument is neither callable nor iterable. After ruling out callable (handled earlier) and failing iter(), pytest rejects the value outright because it cannot be consumed as a source of ids.

Solutions

  1. Pass ids as a list/tuple of strings or a callable.
  2. If you meant a single id for all params, use pytest.param(..., id=...) per parameter set instead.
  3. Remove the ids argument to let pytest auto-generate ids.

Example fix

// before
@pytest.mark.parametrize("x", [1,2,3], ids=42)
// after
@pytest.mark.parametrize("x", [1,2,3], ids=["one","two","three"])
Defensive patterns

Strategy: type-guard

Validate before calling

if not callable(ids) and not isinstance(ids, (list, tuple)):
    try:
        iter(ids)
    except TypeError:
        raise TypeError('ids must be callable or iterable')
@pytest.mark.parametrize('x', values, ids=ids)

Type guard

def is_valid_ids(ids: object) -> bool:
    return callable(ids) or isinstance(ids, (list, tuple)) or hasattr(ids, '__iter__')

Prevention

When it happens

Trigger: Passing ids=42 (an int), ids=True, or ids=SomeObject to @pytest.mark.parametrize; passing a non-iterable, non-callable value by mistake.

Common situations: Refactoring where ids used to be a list and became a single value; boolean flags accidentally passed as ids; copy-paste errors.

Related errors


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

Appendix: source

Thrown at src/_pytest/python.py:1553

            idfn,
            ids_,
            self.config,
            nodeid=nodeid,
        )
        return id_maker.make_unique_parameterset_ids()

    def _validate_ids(
        self,
        ids: Iterable[object | None],
        parametersets: Sequence[ParameterSet],
    ) -> list[object | None]:
        try:
            num_ids = len(ids)  # type: ignore[arg-type]
        except TypeError:
            try:
                iter(ids)
            except TypeError as e:
                raise TypeError("ids must be a callable or an iterable") from e
            num_ids = len(parametersets)

        # num_ids == 0 is a special case: https://github.com/pytest-dev/pytest/issues/1849
        if num_ids != len(parametersets) and num_ids != 0:
            nodeid = self.definition.nodeid
            fail(
                f"In {nodeid}: {len(parametersets)} parameter sets specified, with different number of ids: {num_ids}",
                pytrace=False,
            )

        return list(itertools.islice(ids, num_ids))

    def _validate_if_using_arg_names(
        self,
        argnames: Sequence[str],
        indirect: bool | Sequence[str],
    ) -> None:
        """Check if all argnames are being used, by default values, or directly/indirectly.

View on GitHub (pinned to 0d6fbdeffa)