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
- Store the handler_id returned by add_handler() and pass exactly that value to remove_handler(); never reuse an ID after removal.
- Before removing, check membership: `if handler_id in registry._handlers` — or better, track removed IDs in your own set.
- Use clear() instead of individual remove_handler() calls if you want to remove all handlers at once.
- 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
- Store handler IDs in a set and discard after removal to prevent double-removal.
- Use clear() to remove all handlers at once when appropriate.
- Null out handler ID variables after removal.
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
- Extra header '{name}' not found
- Port must be a number. Received:'${port}'
- Event '{event}' not found. Available events: {self._availabl
- Unsupported DOM mutation type(s) {sorted(unknown)}; expected
- mutation_types must name at least one mutation type
AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14).
Data as JSON: /api/errors/c52f246570a82558.
Report an issue: GitHub.