SeleniumHQ/selenium · warning · ValueError

{self._label.capitalize()} '{handler_id}' not found

Error message

{self._label.capitalize()} '{handler_id}' not found

What it means

Raised as a ValueError by _HandlerRegistry.remove_handler() when the handler_id argument is not present in the internal _handlers dictionary. Each add_handler() call returns a unique handler_id string (formatted as '{prefix}-{counter}'); remove_handler pops from _handlers and throws if the key does not exist. The _label attribute (e.g. 'request handler', 'response handler') is capitalized into the message to identify which handler type was not found.

Source

Thrown at py/private/_network_handlers.py:715

        if isinstance(url_patterns, str):
            url_patterns = [url_patterns]
        patterns = list(url_patterns) if url_patterns else None
        bidi_patterns = globs_to_url_patterns(patterns)
        intercept_result = self._network._add_intercept(phases=[self._phase], url_patterns=bidi_patterns)
        intercept_id = intercept_result.get("intercept") if intercept_result else None
        if self._subscription_callback_id is None:
            self._subscription_callback_id = self._network.add_event_handler(self._event_name, self._on_event)
        self._counter += 1
        handler_id = f"{self._id_prefix}-{self._counter}"
        self._handlers[handler_id] = _HandlerEntry(handler_id, patterns, callback, intercept_id)
        logger.debug("Added %s %s (patterns=%s)", self._label, handler_id, patterns)
        return handler_id

    def remove_handler(self, handler_id: str) -> None:
        """Remove a handler and its intercept by handler ID."""
        entry = self._handlers.pop(handler_id, None)
        if entry is None:
            raise ValueError(f"{self._label.capitalize()} '{handler_id}' not found")
        if entry.intercept_id:
            self._network._remove_intercept(entry.intercept_id)
        if not self._keep_subscription() and self._subscription_callback_id is not None:
            self._network.remove_event_handler(self._event_name, self._subscription_callback_id)
            self._subscription_callback_id = None
        logger.debug("Removed %s %s", self._label, handler_id)

    def clear(self) -> None:
        """Remove all registered handlers and their intercepts."""
        for handler_id in list(self._handlers):
            self.remove_handler(handler_id)

    def intercept_ids(self) -> set:
        """Intercept IDs owned by this registry's handlers."""
        return {entry.intercept_id for entry in self._handlers.values() if entry.intercept_id}

    def _keep_subscription(self) -> bool:
        """Whether the event subscription is still needed."""

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Store the handler_id returned by add_handler() and pass exactly that value to remove_handler(); never reuse an ID after removal.
  2. Before removing, check membership: `if handler_id in registry._handlers` — or better, track removed IDs in your own set.
  3. Use clear() instead of individual remove_handler() calls if you want to remove all handlers at once.
  4. Guard against double-removal by setting the stored handler_id to None after a successful removal.

Example fix

# before
handler_id = registry.add_handler(['*'], callback)
registry.remove_handler(handler_id)
registry.remove_handler(handler_id)  # ValueError

# after
handler_id = registry.add_handler(['*'], callback)
registry.remove_handler(handler_id)
handler_id = None  # prevent double-removal
if handler_id is not None:
    registry.remove_handler(handler_id)
Defensive patterns

Strategy: validation

Validate before calling

# Track handler IDs and check before removing
active_handlers = set()
hid = registry.add_handler(['*'], callback)
active_handlers.add(hid)
if hid in active_handlers:
    registry.remove_handler(hid)
    active_handlers.discard(hid)

Try / catch

try:
    registry.remove_handler(handler_id)
except ValueError as e:
    if 'not found' in str(e):
        # already removed or never added; safe to ignore
        pass
    else:
        raise

Prevention

When it happens

Trigger: Calling remove_handler(handler_id) with an ID that was never returned by add_handler, was already removed in a prior call, or was invalidated by a clear() call. The _handlers.pop(handler_id, None) returns None, triggering the raise.

Common situations: Calling remove_handler twice with the same ID (double-removal); storing the handler_id in a variable that gets overwritten or lost; calling remove_handler after clear() which already removed all handlers; a typo or wrong variable passed as handler_id.

Related errors


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