{"record":{"id":"95674593839af9f8","repo":"Textualize/textual","slug":"unable-to-add-option-r-due-to-duplicate-id","errorCode":null,"errorMessage":"Unable to add {option!r} due to duplicate ID","messagePattern":"Unable to add (.+?) due to duplicate ID","errorType":"exception","errorClass":"DuplicateID","httpStatus":null,"severity":"error","filePath":"src/textual/widgets/_option_list.py","lineNumber":406,"sourceCode":"            # which would update the previous option.\n            # This is sub-optimal, but hopefully not a common occurrence\n            self._clear_caches()\n        options = self._options\n        add_option = self._options.append\n\n        for prompt in new_options:\n            if isinstance(prompt, Option):\n                option = prompt\n            elif prompt is None:\n                if options:\n                    options[-1]._divider = True\n                continue\n            else:\n                option = Option(prompt)\n            self._option_to_index[option] = len(options)\n            if option._id is not None:\n                if option._id in self._id_to_option:\n                    raise DuplicateID(f\"Unable to add {option!r} due to duplicate ID\")\n                self._id_to_option[option._id] = option\n            add_option(option)\n        if self.is_mounted:\n            self.refresh(layout=self.styles.auto_dimensions)\n            self._update_lines()\n        return self\n\n    def add_option(self, option: Option | VisualType | None = None) -> Self:\n        \"\"\"Add a new option to the end of the option list.\n\n        Args:\n            option: New option to add, or `None` for a separator.\n\n        Returns:\n            The `OptionList` instance.\n\n        Raises:\n            DuplicateID: If there is an attempt to use a duplicate ID.","sourceCodeStart":388,"sourceCodeEnd":424,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/widgets/_option_list.py#L388-L424","documentation":"Raised by OptionList.add_options (and add_option) when a new Option carries an id that is already present in the list. OptionList maintains an _id_to_option mapping that must stay unique, so a duplicate id cannot be added. The error occurs during bulk construction via __init__ or set_options as well.","triggerScenarios":"Calling OptionList(options=[Option('a', id='x'), Option('b', id='x')]), or calling add_option(Option('c', id='x')) when 'x' already exists; also set_options with a list containing repeated ids.","commonSituations":"Generating option lists from data where ids are not guaranteed unique (e.g. database rows with repeated keys), copy-pasting Option definitions, or using the same string for every option's id in a loop.","solutions":["Ensure each Option passed to OptionList has a unique id, or omit the id entirely when you don't need lookups by id.","Dedupe/derive ids from your data before constructing: e.g. use enumerate or unique keys.","If you intend to replace options, call clear_options() or use set_options() with a fresh deduplicated list instead of add_option."],"exampleFix":"# before\nOptionList([Option(str(r), id=r['code']) for r in rows])  # codes repeat\n# after\nOptionList([Option(str(r), id=f\"{r['code']}-{i}\") for i, r in enumerate(rows)])","handlingStrategy":"validation","validationCode":"ids = [o.id for o in options if isinstance(o, Option) and o.id is not None]\nif len(ids) != len(set(ids)):\n    raise ValueError('duplicate option ids')","typeGuard":null,"tryCatchPattern":"from textual.widgets.option_list import DuplicateID\ntry:\n    option_list.add_option(new_option)\nexcept DuplicateID:\n    ...  # skip or regenerate id","preventionTips":["Generate ids from unique keys or enumeration","Dedupe data before building the option list","Use set_options on a validated list rather than incremental add_option"],"tags":["optionlist","duplicate-id","textual","widget"],"backgroundTag":"duplicate-id","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}