ReactiveX/RxJava · warning · NoSuchElementException

No more elements

Error message

No more elements

What it means

Thrown by Iterator.next() from Flowable.blockingNext() when the upstream has completed and there is no further item to deliver. It signals a normal terminal condition of the blocking iterator: next() was called after the source Flowable signalled onComplete (or after the last buffered Notification was consumed and hasNext() is now false).

Source

Thrown at src/main/java/io/reactivex/rxjava4/internal/operators/flowable/BlockingFlowableNext.java:122

            } catch (InterruptedException e) {
                subscriber.dispose();
                error = e;
                throw ExceptionHelper.wrapOrThrow(e);
            }
        }

        @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 NextSubscriber<T> extends DisposableSubscriber<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. Always guard next() with hasNext(): while (it.hasNext()) { T v = it.next(); ... }
  2. Verify the upstream Flowable emits the expected count (use test() assertions) before relying on blocking iteration.
  3. If an empty result is legitimate, catch NoSuchElementException and supply a default instead of treating it as a failure.
  4. Add an isEmpty check via Flowable.isEmpty().blockingGet() before iterating if a non-empty contract is required.

Example fix

// before
Iterator<T> it = flowable.blockingNext().iterator();
T first = it.next(); // throws if source empty
// after
Iterator<T> it = flowable.blockingNext().iterator();
if (!it.hasNext()) { return defaultValue; }
T first = it.next();
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate non-empty before consuming the blocking next-iterator.
boolean nonEmpty = flowable.isEmpty().blockingGet(); // false => has at least one
Iterator<T> it = flowable.blockingNext().iterator();
if (!it.hasNext()) { /* source is empty/complete */ }

Try / catch

Iterator<T> it = flowable.blockingNext().iterator();
T value;
if (it.hasNext()) {
    value = it.next();
} else {
    // handle terminal/empty state
    value = defaultValue;
}
// Alternatively, only if a default cannot be structured in:
try {
    while (it.hasNext()) { T v = it.next(); /* consume */ }
} catch (NoSuchElementException e) {
    // safe: loop is hasNext-guarded, but guard against stray next() calls
}

Prevention

When it happens

Trigger: Calling next() without guarding hasNext() after the source completes; a loop that assumes N items but the source emitted fewer; the upstream completes immediately (e.g. Flowable.empty()) and next() is invoked once.

Common situations: Parsing/flattening a finite stream via blockingNext() and miscounting expected elements; an upstream that completes early due to a filter that removes all elements; off-by-one in a manual index counter driving next() calls.

Related errors


AI-assisted analysis of ReactiveX/RxJava@a8ab535614 (2026-08-13). Data as JSON: /api/errors/81777e6311817a5c. Report an issue: GitHub.