{"record":{"id":"9d8eca5238391071","repo":"pytest-dev/pytest","slug":"self-inipath-config-option-name-expects-one","errorCode":null,"errorMessage":"{self.inipath}: config option '{name}' expects one of {_ini_type_repr(type)}, got {builtins.type(value).__name__}: {value!r}","messagePattern":"(.+?): config option '(.+?)' expects one of (.+?), got (.+?): (.+?)","errorType":"exception","errorClass":"UsageError","httpStatus":null,"severity":"error","filePath":"src/_pytest/config/__init__.py","lineNumber":1843,"sourceCode":"        mode = selected.mode\n\n        # An invalid value is a user error, raised as UsageError so that it is\n        # reported as a short message rather than an internal error traceback.\n        try:\n            if not isinstance(type, tuple):\n                return self._getini_value(\n                    mode, name, canonical_name, type, value, default\n                )\n\n            # Union: try each member; the first one that accepts the value wins.\n            for member in type:\n                try:\n                    return self._getini_value(\n                        mode, name, canonical_name, member, value, default\n                    )\n                except (TypeError, ValueError):\n                    pass\n            raise TypeError(\n                f\"{self.inipath}: config option '{name}' expects one of \"\n                f\"{_ini_type_repr(type)}, got {builtins.type(value).__name__}: {value!r}\"\n            )\n        except (TypeError, ValueError) as e:\n            raise UsageError(str(e)) from e\n\n    def _getini_value(\n        self,\n        mode: Literal[\"ini\", \"toml\"],\n        name: str,\n        canonical_name: str,\n        type: str | _IniLiteral,\n        value: object,\n        default: Any,\n    ):\n        \"\"\"Convert a config value, read in the given mode, to the option's type.\"\"\"\n        if isinstance(type, _IniLiteral):\n            # A Literal value is a plain string checked against the registered","sourceCodeStart":1825,"sourceCodeEnd":1861,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/config/__init__.py#L1825-L1861","documentation":"Raised by _getini() at config/__init__.py:1843-1846 when an ini option registered with a union type (e.g. int | str) receives a value that matches NONE of the union members. The loop at capture.py:1836-1842 tries each member's _getini_value; if all raise TypeError/ValueError, the trailing raise at config/__init__.py:1843 fires. The outer try/except at capture.py:1847-1848 re-raises as UsageError so the user sees a clean message.","triggerScenarios":"A plugin registers addini('foo', type=int | str) and the user provides a value in pyproject.toml that is neither an int nor parseable as an int string (e.g. a list or a bool). The union loop fails all members and capture.py:1843 raises.","commonSituations":"pyproject.toml config providing a TOML list/bool where the option expects int or str; ini option type widened to a union but old configs still supplying incompatible values; toml-mode parsing surfacing native types that ini mode would have treated as strings.","solutions":["Provide a value matching one of the union members (typically a string or integer)","Quote the value if you want it treated as a string in toml mode","If you control the plugin, register the union more permissively (e.g. int | str | bool)","Move the option to a CLI flag where argparse can do the type coercion"],"exampleFix":"// before\n# pyproject.toml\n[tool.pytest.ini_options]\nfoo = true   # addini type is int | str\n// after\nfoo = 42     # or\nfoo = \"true\"","handlingStrategy":"validation","validationCode":"# Before writing pyproject.toml programmatically, validate against the union\nimport tomllib\ncfg = tomllib.loads(open('pyproject.toml').read())\nopts = cfg.get('tool',{}).get('pytest',{}).get('ini_options',{})\nfor k, v in opts.items():\n    # plugin must expose its accepted types; check docs/source\n    pass","typeGuard":"def matches_union(value, members) -> bool:\n    for m in members:\n        if m == 'int' and isinstance(value, int): return True\n        if m == 'str' and isinstance(value, str): return True\n        if m == 'bool' and isinstance(value, bool): return True\n        if m == 'float' and isinstance(value, (int, float)): return True\n    return False","tryCatchPattern":"from _pytest.config import UsageError\ntry:\n    val = config.getini('opt')\nexcept UsageError as e:\n    print(f'Bad value: {e}')","preventionTips":["Quote TOML values you want treated as strings","Document each union member your plugin accepts","Test pyproject.toml configs in CI with the actual installed plugin versions"],"tags":["config","ini","toml","validation","type"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}