SeleniumHQ/selenium · error · ValueError

mutation_types must name at least one mutation type

Error message

mutation_types must name at least one mutation type

What it means

Raised as a ValueError by _DOMMutationHandler._normalize_types() when the normalized set of mutation types is empty. After converting the input to a frozenset and validating that all values are in MUTATION_TYPES, the code checks `if not types` and throws if the frozenset has zero elements. This is distinct from the unknown-type error (110) — here all values were valid but the collection was empty.

Source

Thrown at py/private/_script_handlers.py:541

        self._channel: str | None = None
        self._subscription_id: str | None = None
        self._handlers: dict[int, frozenset[str]] = {}
        self._preload_script_ids: list[str] = []
        self._active_types: set[str] = set()

    def _normalize_types(self, mutation_types: str | Iterable[str] | None) -> frozenset[str]:
        if mutation_types is None:
            return frozenset(self.DEFAULT_MUTATION_TYPES)
        if isinstance(mutation_types, str):
            mutation_types = (mutation_types,)
        types = frozenset(mutation_types)
        unknown = types - set(self.MUTATION_TYPES)
        if unknown:
            raise ValueError(
                f"Unsupported DOM mutation type(s) {sorted(unknown)}; expected a subset of {self.MUTATION_TYPES}"
            )
        if not types:
            raise ValueError("mutation_types must name at least one mutation type")
        return types

    def _channel_argument(self) -> dict:
        if self._channel is None:
            # Stable, namespaced channel to avoid collisions with user scripts.
            self._channel = f"selenium.domMutation.{uuid.uuid4().hex}"
        return {"type": "channel", "value": {"channel": self._channel}}

    def _listener_declaration(self, types: set[str]) -> str:
        # script.addPreloadScript arguments may only be channels, so the
        # observation options are inlined into the function declaration.
        options = json.dumps({name: True for name in sorted(types)})
        return "function(channel) { return (" + DOM_MUTATION_LISTENER_JS + ")(channel, " + options + "); }"

    def _observe_types(self, channel_arg: dict, types: set[str]) -> None:
        declaration = self._listener_declaration(types)
        preload_script_id = self._script._add_preload_script(declaration, arguments=[channel_arg])
        self._preload_script_ids.append(preload_script_id)

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Pass None instead of an empty list to accept the default mutation types (attributes).
  2. Ensure the mutation_types collection always contains at least one valid value before passing it.
  3. Add a guard: if your code computes the list, check `if not types: types = None` before the call.

Example fix

# before
handler.add_callback(callback, mutation_types=[])

# after
handler.add_callback(callback, mutation_types=None)  # uses default 'attributes'
# or
handler.add_callback(callback, mutation_types=['attributes'])
Defensive patterns

Strategy: validation

Validate before calling

if not mutation_types:
    mutation_types = None  # accept default
handler.add_callback(callback, mutation_types=mutation_types)

Try / catch

try:
    handler.add_callback(callback, mutation_types=types)
except ValueError as e:
    if 'must name at least one' in str(e):
        types = ['attributes']
        handler.add_callback(callback, mutation_types=types)
    else:
        raise

Prevention

When it happens

Trigger: Calling _normalize_types with an empty list/tuple/set: mutation_types=[] or mutation_types=set(). The frozenset of the empty iterable has zero elements, passing the unknown-type check but failing the `if not types` check.

Common situations: Programmatically constructing the mutation_types list from a filter or configuration that can produce an empty set; passing an empty list by accident; a default parameter resolution that yields [] in an edge case.

Related errors


AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14). Data as JSON: /api/errors/d80ae45611c2622c. Report an issue: GitHub.