pytest-dev/pytest · error · TypeError

parameter needs to be a string, but given

Error message

{param} parameter needs to be a string, but {g} given

What it means

_check_record_param_type (junitxml.py:337) validates that the NAME argument passed to record_testsuite_property (and similarly record_property) is a str. If the caller passes a non-string name (int, tuple, etc.), it raises TypeError naming the bad type. The VALUE may be any object; only the name must be a string because it becomes an XML attribute/tag name.

Solutions

  1. Cast the name to str before calling: record_testsuite_property(str(name), value).
  2. Ensure name values are string literals or string-typed variables.
  3. Double-check argument order: (name, value).

Example fix

// before
record_testsuite_property(run_id, 'passed')  # run_id is int
// after
record_testsuite_property(str(run_id), 'passed')
Defensive patterns

Strategy: type-guard

Validate before calling

# Validate name is str before recording a property
def safe_suite_property(record_fn, name, value):
    if not isinstance(name, str):
        raise TypeError(f'name must be str, got {type(name).__name__}')
    record_fn(name, value)

Type guard

def is_property_name(v) -> bool:
    return isinstance(v, str) and v.isidentifier()

Prevention

When it happens

Trigger: Calling record_testsuite_property(123, 'v') or record_testsuite_property(('a','b'), 'v'); passing a variable expected to be a string but holding an int; looping over keys without casting.

Common situations: Programmatically generating property names from counters/enums without str(); mixing up argument order (passing value first); refactoring that changes name type.

Related errors


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

Appendix: source

Thrown at src/_pytest/junitxml.py:337

        pass

    attr_func = add_attr_noop

    xml = request.config.stash.get(xml_key, None)
    if xml is not None:
        node_reporter = xml.node_reporter(request.node.nodeid)
        attr_func = node_reporter.add_attribute

    return attr_func


def _check_record_param_type(param: str, v: str) -> None:
    """Used by record_testsuite_property to check that the given parameter name is of the proper
    type."""
    __tracebackhide__ = True
    if not isinstance(v, str):
        msg = "{param} parameter needs to be a string, but {g} given"  # type: ignore[unreachable]
        raise TypeError(msg.format(param=param, g=type(v).__name__))


@pytest.fixture(scope="session")
def record_testsuite_property(request: FixtureRequest) -> Callable[[str, object], None]:
    """Record a new ``<property>`` tag as child of the root ``<testsuite>``.

    This is suitable to writing global information regarding the entire test
    suite, and is compatible with ``xunit2`` JUnit family.

    This is a ``session``-scoped fixture which is called with ``(name, value)``. Example:

    .. code-block:: python

        def test_foo(record_testsuite_property):
            record_testsuite_property("ARCH", "PPC")
            record_testsuite_property("STORAGE_TYPE", "CEPH")

    :param name:

View on GitHub (pinned to 0d6fbdeffa)