{"record":{"id":"aaeea2877ccafd18","repo":"pytest-dev/pytest","slug":"self-inipath-config-option-name-expects-one-aaeea2","errorCode":null,"errorMessage":"{self.inipath}: config option '{name}' expects one of {_ini_type_repr(type)}, got {value!r}","messagePattern":"(.+?): config option '(.+?)' expects one of (.+?), got (.+?)","errorType":"exception","errorClass":"UsageError","httpStatus":null,"severity":"error","filePath":"src/_pytest/config/__init__.py","lineNumber":1869,"sourceCode":"        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\n            # choices, without coercion, in both ini and toml modes.\n            if not isinstance(value, str):\n                raise TypeError(\n                    f\"{self.inipath}: config option '{name}' expects a string, \"\n                    f\"got {builtins.type(value).__name__}: {value!r}\"\n                )\n            if value not in type.choices:\n                raise ValueError(\n                    f\"{self.inipath}: config option '{name}' expects one of \"\n                    f\"{_ini_type_repr(type)}, got {value!r}\"\n                )\n            return value\n        if mode == \"ini\":\n            # In ini mode, values are always str | list[str].\n            assert isinstance(value, (str, list))\n            return self._getini_ini(name, canonical_name, type, value, default)\n        elif mode == \"toml\":\n            return self._getini_toml(name, canonical_name, type, value, default)\n        else:\n            assert_never(mode)\n\n    def _getini_ini(\n        self,\n        name: str,\n        canonical_name: str,\n        type: str,","sourceCodeStart":1851,"sourceCodeEnd":1887,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/config/__init__.py#L1851-L1887","documentation":"Raised by _getini_value() at config/__init__.py:1868-1872 when an ini option registered with a Literal[...] type receives a string that is NOT one of the registered choices. _IniLiteral.choices (argparsing.py:50) holds the allowed strings; capture.py:1868 checks membership and raises ValueError, which is converted to UsageError by the outer handler at config/__init__.py:1847-1848. _ini_type_repr() (argparsing.py:95) renders the allowed choices in the message.","triggerScenarios":"A plugin registers addini('mode', type=Literal['a','b','c']) and the user writes `mode = 'd'`. capture.py:1868 returns False for membership and capture.py:1869-1872 raises.","commonSituations":"Typos in option values; using a value valid in an older plugin version that was since removed from the Literal; case-sensitivity mismatches (e.g. 'Debug' vs 'DEBUG'); copy-paste from another tool's config.","solutions":["Use one of the choices listed in the error message (rendered by _ini_type_repr)","Check the plugin's changelog if a previously valid choice was removed","Match case exactly - Literal membership is case-sensitive string equality","If you need more flexibility, request the plugin author switch from Literal to a free-form string with custom validation"],"exampleFix":"// before\n# pytest.ini\n[pytest]\nlog_cli_level = VERBOSE\n// after\n[pytest]\nlog_cli_level = DEBUG","handlingStrategy":"validation","validationCode":"ALLOWED = {'DEBUG','INFO','WARNING','ERROR','CRITICAL'}  # example\nif value not in ALLOWED:\n    raise ValueError(f'log_level must be one of {ALLOWED}, got {value!r}')","typeGuard":"def in_choices(value: str, choices) -> bool:\n    return value in choices","tryCatchPattern":"from _pytest.config import UsageError\ntry:\n    val = config.getini('mode')\nexcept UsageError as e:\n    # message lists valid choices via _ini_type_repr\n    print(e)","preventionTips":["Copy choice values verbatim from plugin docs","Match case exactly - Literal membership is case-sensitive","When upgrading plugins, re-check the Literal choices in their changelog"],"tags":["config","ini","literal","validation","choices"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}