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

  1. Deduplicate by id before adding: filter to first occurrence per id.
  2. For refreshes, use set_options (replacing all) instead of add_options, or track existing ids.
  3. Fix the upstream data to guarantee unique ids.
  4. 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

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


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