Textualize/textual · error · DuplicateID
Unable to add {option!r} due to duplicate ID
Error message
Unable to add {option!r} due to duplicate ID What it means
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.
Source
Thrown at src/textual/widgets/_option_list.py:406
# which would update the previous option.
# This is sub-optimal, but hopefully not a common occurrence
self._clear_caches()
options = self._options
add_option = self._options.append
for prompt in new_options:
if isinstance(prompt, Option):
option = prompt
elif prompt is None:
if options:
options[-1]._divider = True
continue
else:
option = Option(prompt)
self._option_to_index[option] = len(options)
if option._id is not None:
if option._id in self._id_to_option:
raise DuplicateID(f"Unable to add {option!r} due to duplicate ID")
self._id_to_option[option._id] = option
add_option(option)
if self.is_mounted:
self.refresh(layout=self.styles.auto_dimensions)
self._update_lines()
return self
def add_option(self, option: Option | VisualType | None = None) -> Self:
"""Add a new option to the end of the option list.
Args:
option: New option to add, or `None` for a separator.
Returns:
The `OptionList` instance.
Raises:
DuplicateID: If there is an attempt to use a duplicate ID.View on GitHub (pinned to 06dbeef4bb)
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.
Example fix
# before
OptionList([Option(str(r), id=r['code']) for r in rows]) # codes repeat
# after
OptionList([Option(str(r), id=f"{r['code']}-{i}") for i, r in enumerate(rows)]) Defensive patterns
Strategy: validation
Validate before calling
ids = [o.id for o in options if isinstance(o, Option) and o.id is not None]
if len(ids) != len(set(ids)):
raise ValueError('duplicate option ids') Try / catch
from textual.widgets.option_list import DuplicateID
try:
option_list.add_option(new_option)
except DuplicateID:
... # skip or regenerate id Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Tried to insert a widget with ID {widget_id!r}, but a widget
- New options contain duplicated IDs; Ensure that the IDs are
- No {name!r} key in COMPONENT_CLASSES
- A widget can't be its own parent
- Widget positional arguments must be Widget subclasses; not {
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/95674593839af9f8.
Report an issue: GitHub.