alibaba/nacos · warning · NoSuchElementException
The subscriber has no event publisher
Error message
The subscriber has no event publisher
What it means
Thrown by NotifyCenter when unregistering a subscriber that has no associated publisher: the subscriber's subscribeType is not a SlowEvent, it is not in the shared publisher, and removeSubscriber returned false. NoSuchElementException indicates a logic error in subscriber registration/unregistration pairing rather than a transport problem.
Source
Thrown at common/src/main/java/com/alibaba/nacos/common/notify/NotifyCenter.java:251
if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) {
INSTANCE.sharePublisher.removeSubscriber(consumer, subscribeType);
} else {
removeSubscriber(consumer, subscribeType);
}
}
return;
}
final Class<? extends Event> subscribeType = consumer.subscribeType();
if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) {
INSTANCE.sharePublisher.removeSubscriber(consumer, subscribeType);
return;
}
if (removeSubscriber(consumer, subscribeType)) {
return;
}
throw new NoSuchElementException("The subscriber has no event publisher");
}
/**
* Remove subscriber.
*
* @param consumer subscriber instance.
* @param subscribeType subscribeType.
* @return whether remove subscriber successfully or not.
*/
private static boolean removeSubscriber(final Subscriber consumer,
Class<? extends Event> subscribeType) {
final String topic = ClassUtils.getCanonicalName(subscribeType);
EventPublisher eventPublisher = INSTANCE.publisherMap.get(topic);
if (null == eventPublisher) {
return false;
}
if (eventPublisher instanceof ShardedEventPublisher) {View on GitHub (pinned to 9b989acdf1)
Solutions
- Match the subscribeType passed to removal with the one used at registration; reuse the same Subscriber instance.
- Guard removal: track registered subscribers and only call deregister for ones you actually registered.
- Wrap deregistration in try/catch(NoSuchElementException) during teardown so a mismatch does not abort cleanup of other subscribers.
- Register/unregister symmetrically in component lifecycle start/stop methods.
Example fix
// before
NotifyCenter.deregisterSubscriber(sub); // throws if sub was never registered for its type
// after
try {
NotifyCenter.deregisterSubscriber(sub);
} catch (NoSuchElementException e) {
LOG.debug("Subscriber {} already removed for {}", sub, sub.subscribeType());
} Defensive patterns
Strategy: try-catch
Validate before calling
if (registeredSubscribers.remove(consumer)) {
NotifyCenter.deregisterSubscriber(consumer);
} Try / catch
try {
NotifyCenter.deregisterSubscriber(consumer);
} catch (NoSuchElementException e) {
log.debug("Subscriber {} not registered, ignoring", consumer);
} Prevention
- Track the subscribers you register and only deregister those.
- Use the same Subscriber instance and subscribeType at register and deregister time.
- Make teardown idempotent by catching NoSuchElementException during cleanup.
When it happens
Trigger: Calling NotifyCenter.deregisterSubscriber(consumer)/removeSubscriber for a subscriber/subscribeType combination that was never registered, was already removed, or was registered under a different event type than the one passed at removal.
Common situations: Double-unregistering the same subscriber; unregistering with a subscribeType that differs from the one used at registration; subscriber registered via the SlowEvent shared path but removed via the per-type path (or vice versa); cleanup code running for components that were never wired.
Related errors
AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14).
Data as JSON: /api/errors/8d56f576e43f340c.
Report an issue: GitHub.