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

  1. Guard next() with hasNext() and terminate the loop when hasNext() returns false.
  2. Verify expected counts with Observable.test().assertValueCount(n) before relying on blocking iteration.
  3. Catch NoSuchElementException to return a default for legitimately empty sources.
  4. 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

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.