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

  1. Ensure each Option passed to OptionList has a unique id, or omit the id entirely when you don't need lookups by id.
  2. Dedupe/derive ids from your data before constructing: e.g. use enumerate or unique keys.
  3. 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

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


AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27). Data as JSON: /api/errors/95674593839af9f8. Report an issue: GitHub.