ReactiveX/RxJava · warning · UnsupportedOperationException

Read only iterator

Error message

Read only iterator

What it means

Thrown by the Iterator returned from Flowable.blockingMostRecent() when its remove() method is called. Blocking operators expose the live stream as a read-only Iterator over the most-recently-emitted value, so mutation via remove() is forbidden by design and always throws UnsupportedOperationException. The iterator reflects a push-based source, so there is no backing collection to remove from.

Source

Thrown at src/main/java/io/reactivex/rxjava4/internal/operators/flowable/BlockingFlowableMostRecent.java:115

                    if (buf == null) {
                        buf = value;
                    }
                    if (NotificationLite.isComplete(buf)) {
                        throw new NoSuchElementException();
                    }
                    if (NotificationLite.isError(buf)) {
                        throw ExceptionHelper.wrapOrThrow(NotificationLite.getError(buf));
                    }
                    return NotificationLite.getValue(buf);
                }
                finally {
                    buf = null;
                }
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException("Read only iterator");
            }
        }
    }
}

View on GitHub (pinned to a8ab535614)

Solutions

  1. Remove the it.remove() call from your loop; blocking RxJava iterators are read-only views and never support removal.
  2. If you need to filter, apply Flowable.filter(...) before blockingMostRecent() instead of mutating the iterator.
  3. If a generic helper mandates remove(), wrap the iterator with your own delegating Iterator whose remove() is a no-op.
  4. Collect results into a separate mutable list rather than trying to mutate the source iterator.

Example fix

// before
Iterator<T> it = flowable.blockingMostRecent(initial).iterator();
while (it.hasNext()) { it.next(); it.remove(); }
// after
Iterator<T> it = flowable.blockingMostRecent(initial).iterator();
while (it.hasNext()) { T v = it.next(); /* use v, never remove() */ }
Defensive patterns

Strategy: validation

Validate before calling

// Never call remove() on blocking RxJava iterators.
// Before your loop, confirm the iterator is read-only by design (no runtime check needed);
// simply omit it.remove().
Iterator<T> it = flowable.blockingMostRecent(initial).iterator();
while (it.hasNext()) {
    T v = it.next();
    // process v; do NOT call it.remove()
}

Prevention

When it happens

Trigger: Calling Iterator.remove() on the iterator obtained from Flowable.blockingMostRecent(initialValue).next() iteration loop; using a generic utility that defensively invokes remove() on any iterator it consumes; Java for-each is fine, but explicit it.remove() after it.next() triggers it.

Common situations: Migrating imperative collection-processing code (that used List.remove() semantics) onto a blocking RxJava iterator; helper libraries that call remove() as part of cleanup; copy-paste of a loop template that included it.remove().

Related errors


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