pytest-dev/pytest · error · TypeError

{self.inipath}: config option '{name}' expects a string, got

Error message

{self.inipath}: config option '{name}' expects a string, got {builtins.type(value).__name__}: {value!r}

What it means

Raised by Config._getini_value (config/__init__.py:1860-1867) when an ini option is registered with a Literal/choice type (an _IniLiteral carrying .choices) but the value read from config is not a Python str. Literal choices are checked verbatim with no coercion, in both ini and toml modes, so a non-string (e.g. a TOML int) is rejected before the choice membership test.

Source

Thrown at src/_pytest/config/__init__.py:1864

            )
        except (TypeError, ValueError) as e:
            raise UsageError(str(e)) from e

    def _getini_value(
        self,
        mode: Literal["ini", "toml"],
        name: str,
        canonical_name: str,
        type: str | _IniLiteral,
        value: object,
        default: Any,
    ):
        """Convert a config value, read in the given mode, to the option's type."""
        if isinstance(type, _IniLiteral):
            # A Literal value is a plain string checked against the registered
            # choices, without coercion, in both ini and toml modes.
            if not isinstance(value, str):
                raise TypeError(
                    f"{self.inipath}: config option '{name}' expects a string, "
                    f"got {builtins.type(value).__name__}: {value!r}"
                )
            if value not in type.choices:
                raise ValueError(
                    f"{self.inipath}: config option '{name}' expects one of "
                    f"{_ini_type_repr(type)}, got {value!r}"
                )
            return value
        if mode == "ini":
            # In ini mode, values are always str | list[str].
            assert isinstance(value, (str, list))
            return self._getini_ini(name, canonical_name, type, value, default)
        elif mode == "toml":
            return self._getini_toml(name, canonical_name, type, value, default)
        else:
            assert_never(mode)

View on GitHub (pinned to 98b357f69e)

Solutions

  1. Quote the value in TOML so it parses as a string: my_opt = "strict".
  2. Verify the value is one of the documented choices for that option.
  3. If the option legitimately accepts non-string types, the plugin should register a non-Literal type (e.g. 'int').

Example fix

# before (pyproject.toml)
[tool.pytest.ini_options]
log_level = 20   # Literal choice expects a string

# after
log_level = "INFO"
Defensive patterns

Strategy: validation

Validate before calling

def ensure_str_choice(value):
    if not isinstance(value, str):
        raise TypeError(f'choice option must be a string, got {type(value).__name__}')
    return value

Type guard

def is_str_value(value) -> bool:
    return isinstance(value, str)

Try / catch

try:
    config.getini(name)
except Exception:
    # quote the value in pyproject.toml and rerun

Prevention

When it happens

Trigger: Registering addini(..., type=..., choices=[...]) and supplying, via TOML, a value of native non-string type (int/bool/list) for that key. getini() or any ini read then trips this branch.

Common situations: pyproject.toml sets a choice option to a bare integer or boolean instead of a quoted string; converting an .ini file to .toml and dropping the quotes; a plugin registering choices but the user supplying the wrong native TOML type.

Related errors


AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04). Data as JSON: /data/errors/8587a2c455b25ae2.json. Report an issue: GitHub.