{"record":{"id":"c2c50d5b5c13cc5d","repo":"pytest-dev/pytest","slug":"lowercase-short-options-are-reserved","errorCode":null,"errorMessage":"lowercase short options are reserved","messagePattern":"lowercase short options are reserved","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/_pytest/config/argparsing.py","lineNumber":483,"sourceCode":"                            f\"option dest {dest!r} already used by \"\n                            f\"{option.names()!r} (this is the option that maps to \"\n                            f\"dest {dest!r}); pass dest={dest!r} explicitly \"\n                            \"to share the destination\"\n                        )\n        self._addoption_inner(opts, attrs, allow_reserved=False)\n\n    def _addoption(self, *opts: str, **attrs: Any) -> None:\n        \"\"\"Like addoption(), but also allows registering short lower case options (e.g. -x),\n        which are reserved for pytest core.\"\"\"\n        self._addoption_inner(opts, attrs, allow_reserved=True)\n\n    def _addoption_inner(\n        self, opts: tuple[str, ...], attrs: dict[str, Any], allow_reserved: bool\n    ) -> None:\n        if not allow_reserved:\n            for opt in opts:\n                if len(opt) >= 2 and opt[0] == \"-\" and opt[1].islower():\n                    raise ValueError(\"lowercase short options are reserved\")\n\n        action = self._arggroup.add_argument(*opts, **attrs)\n        option = Argument(action)\n        self.options.append(option)\n        if self.parser:\n            for name in option.names():\n                self.parser._opt2dest[name] = option.dest\n            self.parser.processoption(option)\n\n\nclass PytestArgumentParser(argparse.ArgumentParser):\n    def __init__(\n        self,\n        usage: str | None,\n        extra_info: dict[str, str],\n        *,\n        prog: str | None = None,\n    ) -> None:","sourceCodeStart":465,"sourceCodeEnd":501,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/config/argparsing.py#L465-L501","documentation":"pytest reserves single-letter lowercase short options (e.g. -x, -q) for its own core. When a plugin author registers an option via the public group.addoption() (not the internal _addoption) and one of the option strings is a lowercase short flag like '-x', pytest rejects it. This keeps the short namespace stable for pytest itself.","triggerScenarios":"Calling group.addoption('-x', ...) (lowercase single letter) from a conftest or plugin. Only paths through addoption()/allow_reserved=False enforce this; pytest core uses _addoption which sets allow_reserved=True.","commonSituations":"Plugin author picking '-c' or '-f' for their flag without realizing it collides with reserved core options; a conftest trying to add a convenience short flag.","solutions":["Use an uppercase short option (e.g. '-X') instead, since uppercase shorts are not reserved.","Use only a long option (e.g. '--myflag') and drop the short form entirely.","If you genuinely need a core short option, you cannot register it; rely on the existing core behavior instead."],"exampleFix":"// before\ngroup.addoption('-x', '--my-x-flag', action='store_true')\n// after\ngroup.addoption('-X', '--my-x-flag', action='store_true')","handlingStrategy":"validation","validationCode":"def is_reserved_short(opt: str) -> bool:\n    return len(opt) >= 2 and opt[0] == '-' and opt[1].islower()\n\nfor o in opts:\n    assert not is_reserved_short(o), f'{o!r} is a reserved lowercase short option'","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Prefer long option names ('--myflag') for plugin/conftest options.","If a short flag is needed, use uppercase ('-M').","Reserve lowercase shorts for pytest core only."],"tags":["argparse","plugins","conftest","reserved-options"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}