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
- Pass ids as a list/tuple of strings or a callable.
- If you meant a single id for all params, use pytest.param(..., id=...) per parameter set instead.
- 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
- Pass ids as a list/tuple of strings or a callable.
- Use pytest.param(..., id=...) for per-set ids.
- Omit ids to use auto-generated ones when unsure.
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
- error raised while trying to determine id of parameter
- Expected id to be a string or a `pytest.HIDDEN_PARAM`…
- ext must not be None
- must be absolute import path string, not
- Unknown parametrize_long_str_id_strategy
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)