pypa/pip · error · ValueError
No subscribers: %r
Error message
No subscribers: %r
What it means
Raised as ValueError by EventMixin.remove when you try to remove a subscriber for an event name that has no subscribers registered at all. The pub/sub dict self._subscribers does not contain the event key, so removal is impossible. It is distinct from the case where the event exists but the specific subscriber is absent (which would raise a different error from deque.remove).
Source
Thrown at src/pip/_vendor/distlib/util.py:1048
if event not in subs:
subs[event] = deque([subscriber])
else:
sq = subs[event]
if append:
sq.append(subscriber)
else:
sq.appendleft(subscriber)
def remove(self, event, subscriber):
"""
Remove a subscriber for an event.
:param event: The name of an event.
:param subscriber: The subscriber to be removed.
"""
subs = self._subscribers
if event not in subs:
raise ValueError('No subscribers: %r' % event)
subs[event].remove(subscriber)
def get_subscribers(self, event):
"""
Return an iterator for the subscribers for an event.
:param event: The event to return subscribers for.
"""
return iter(self._subscribers.get(event, ()))
def publish(self, event, *args, **kwargs):
"""
Publish a event and return a list of values returned by its
subscribers.
:param event: The event to publish.
:param args: The positional arguments to pass to the event's
subscribers.
:param kwargs: The keyword arguments to pass to the event'sView on GitHub (pinned to f399c37189)
Solutions
- Guard removal: if event in obj.get_subscribers(event) or check the event was added.
- Track your own registration flag/set so remove is only called after a successful add.
- Use try/except ValueError around remove for fire-and-forget cleanup.
Example fix
# before
obj.remove('evt', handler)
# after
try:
obj.remove('evt', handler)
except ValueError:
pass Defensive patterns
Strategy: try-catch
Validate before calling
def safe_remove(obj, event, subscriber):
if any(s is subscriber for s in obj.get_subscribers(event)):
obj.remove(event, subscriber) Type guard
def has_subscribers(obj, event) -> bool:
return event in obj._subscribers Try / catch
try:
obj.remove(event, subscriber)
except ValueError as e:
if 'No subscribers' in str(e):
pass # already clean Prevention
- Only call remove after a successful add.
- Track registrations in your own structure.
When it happens
Trigger: Calling EventMixin().remove('my-event', handler) before any add('my-event', handler); removing from an event after all subscribers were already removed and the key cleared; typo in the event name passed to remove.
Common situations: Teardown/cleanup code calling remove unconditionally; event-name mismatches between add and remove; lifecycle ordering bugs where remove runs before add.
Related errors
- %r not a successor of anything
- invalid requirement: %s
- unexpected trailing data: %s
- path '%s' cannot be absolute
- path '%s' cannot end with '/'
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/0fd2a79fbe141cc9.
Report an issue: GitHub.