ReactiveX/RxJava · warning · UnsupportedOperationException

Read only iterator

Error message

Read only iterator

What it means

Thrown by Iterator.remove() on the iterator from Observable.blockingMostRecent(). The iterator replays the most recently emitted Notification (value, completion, or error) and is a read-only view, so remove() always throws UnsupportedOperationException("Read only iterator"). There is no underlying collection to mutate.

Source

Thrown at src/main/java/io/reactivex/rxjava4/internal/operators/observable/BlockingObservableMostRecent.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; this iterator is read-only.
  2. Filter the source upstream with Observable.filter(...) to exclude items before blocking iteration.
  3. Maintain your own mutable collection of retained items instead of mutating the iterator.
  4. Delegate through a no-op remove() wrapper if a dependency requires the method.

Example fix

// before
Iterator<T> it = observable.blockingMostRecent(initial).iterator();
while (it.hasNext()) { T v = it.next(); if (drop(v)) it.remove(); }
// after
Iterator<T> it = observable.blockingMostRecent(initial).iterator();
List<T> kept = new ArrayList<>();
while (it.hasNext()) { T v = it.next(); if (!drop(v)) kept.add(v); }
Defensive patterns

Strategy: validation

Validate before calling

// Read-only iterator: never call remove().
Iterator<T> it = observable.blockingMostRecent(initial).iterator();
while (it.hasNext()) {
    T v = it.next();
    // process v; it.remove() is unsupported
}

Prevention

When it happens

Trigger: Calling it.remove() on the iterator from observable.blockingMostRecent(initial).iterator(); a generic consumer that invokes remove() during drain; ported imperative loop with a remove() step.

Common situations: Legacy collection-processing code that paired next() with remove(); a utility library that calls remove() on any iterator; misreading blockingMostRecent() as a queue you can shrink.

Related errors


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