Textualize/textual · error · DuplicateID
New options contain duplicated IDs; Ensure that the IDs are
Error message
New options contain duplicated IDs; Ensure that the IDs are unique.
What it means
A DuplicateID exception raised by OptionList.add_options (also used by set_options/add_option) when two or more Option entries in the batch share the same non-None id. Option ids must be unique within an OptionList so selections can be mapped back to options unambiguously.
Source
Thrown at src/textual/widgets/_option_list.py:380
def add_options(self, new_options: Iterable[OptionListContent]) -> Self:
"""Add new options.
Args:
new_options: Content of new options.
Returns:
The `OptionList` instance.
"""
new_options = list(new_options)
option_ids = [
option._id
for option in new_options
if isinstance(option, Option) and option._id is not None
]
if len(option_ids) != len(set(option_ids)):
raise DuplicateID(
"New options contain duplicated IDs; Ensure that the IDs are unique."
)
if not new_options:
return self
if new_options[0] is None:
# Handle the case where the first new option is None,
# 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:View on GitHub (pinned to 06dbeef4bb)
Solutions
- Deduplicate by id before adding: filter to first occurrence per id.
- For refreshes, use set_options (replacing all) instead of add_options, or track existing ids.
- Fix the upstream data to guarantee unique ids.
- Catch DuplicateID and report which ids collided.
Example fix
# before option_list.add_options(options) # duplicate ids present # after seen = set() unique = [o for o in options if not (o.id and o.id in seen or seen.add(o.id))] option_list.add_options(unique)
Defensive patterns
Strategy: validation
Validate before calling
seen: set[str] = set() unique = [o for o in options if o.id is None or not (o.id in seen or seen.add(o.id))] option_list.add_options(unique)
Try / catch
from textual.widgets._option_list import DuplicateID
try:
option_list.add_options(options)
except DuplicateID:
# dedupe and retry
seen, unique = set(), []
for o in options:
if o.id is None or o.id not in seen:
if o.id is not None:
seen.add(o.id)
unique.append(o)
option_list.add_options(unique) Prevention
- Deduplicate ids before add_options/set_options
- Use set_options for full refreshes instead of appending
- Guarantee unique ids at the data source (primary keys)
When it happens
Trigger: Passing [Option('a', id='x'), Option('b', id='x')] to add_options; building options from data with duplicate primary keys; calling add_options twice with the same id-bearing options.
Common situations: Generating menu options from database rows or API results containing duplicate ids; concatenating option lists that overlap; refresh routines appending already-present options.
Related errors
- Tried to insert a widget with ID {widget_id!r}, but a widget
- Unable to add {option!r} due to duplicate ID
- 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/44306c07894e48bf.
Report an issue: GitHub.