ReactiveX/RxJava · warning · NoSuchElementException
No more elements
Error message
No more elements
What it means
Thrown by Iterator.next() from Observable.blockingNext() when hasNext() is false because the source completed and no further Notification is available. NoSuchElementException("No more elements") marks the normal terminal state of the blocking next-iterator; calling next() past completion triggers it.
Source
Thrown at src/main/java/io/reactivex/rxjava4/internal/operators/observable/BlockingObservableNext.java:122
if (nextNotification.isOnComplete()) {
return false;
}
error = nextNotification.getError();
throw ExceptionHelper.wrapOrThrow(error);
}
@Override
public T next() {
if (error != null) {
// If any error has already been thrown, throw it again.
throw ExceptionHelper.wrapOrThrow(error);
}
if (hasNext()) {
isNextConsumed = true;
return next;
}
else {
throw new NoSuchElementException("No more elements");
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("Read only iterator");
}
}
static final class NextObserver<T> extends DisposableObserver<Notification<T>> {
private final BlockingQueue<Notification<T>> buf = new ArrayBlockingQueue<>(1);
final AtomicInteger waiting = new AtomicInteger();
@Override
public void onComplete() {
// ignore
}
View on GitHub (pinned to a8ab535614)
Solutions
- Guard next() with hasNext() and terminate the loop when hasNext() returns false.
- Verify expected counts with Observable.test().assertValueCount(n) before relying on blocking iteration.
- Catch NoSuchElementException to return a default for legitimately empty sources.
- Check Observable.isEmpty().blockingGet() first if a non-empty contract is assumed.
Example fix
// before
Iterator<T> it = observable.blockingNext().iterator();
T first = it.next(); // throws if source empty
// after
Iterator<T> it = observable.blockingNext().iterator();
if (!it.hasNext()) { return defaultValue; }
T first = it.next(); Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check non-empty before consuming the blocking next-iterator.
boolean empty = observable.isEmpty().blockingGet();
Iterator<T> it = observable.blockingNext().iterator();
if (!it.hasNext()) { /* source completed empty */ } Try / catch
Iterator<T> it = observable.blockingNext().iterator();
T v;
if (it.hasNext()) {
v = it.next();
} else {
v = defaultValue; // empty completion
}
// Defensive guard for a stray next() after completion:
try { it.next(); }
catch (NoSuchElementException e) { /* terminal */ } Prevention
- Guard next() with hasNext() and stop on false.
- Use Observable.isEmpty().blockingGet() to pre-check a non-empty contract.
- Assert expected emission counts with test() before relying on blocking iteration.
- Return a default on NoSuchElementException when empty completion is acceptable.
When it happens
Trigger: Calling next() after the source completed without a hasNext() guard; upstream that emits fewer items than the consumer's fixed loop expects; Observable.empty() consumed via blockingNext().
Common situations: Miscounted expected emissions during blocking iteration; an upstream that completes early because of a filter or take(); off-by-one in an index-driven next() loop.
Related errors
AI-assisted analysis of ReactiveX/RxJava@a8ab535614 (2026-08-13).
Data as JSON: /api/errors/1b0b803b7a3ceba5.
Report an issue: GitHub.