ReactiveX/RxJava · error · UnsupportedOperationException

Read-only iterator.

Error message

Read-only iterator.

What it means

Thrown as UnsupportedOperationException('Read-only iterator.') by the Iterator returned by BlockingFlowableLatest when remove() is called. BlockingFlowableLatest exposes only the most recent item from the flowable as a blocking iterator; the underlying sequence is not a mutable collection, so remove() is unsupported by design. The message is more descriptive than the sibling BlockingFlowableIterable iterator but the contract is identical.

Source

Thrown at src/main/java/io/reactivex/rxjava4/internal/operators/flowable/BlockingFlowableLatest.java:114

            }
            return iteratorNotification.isOnNext();
        }

        @Override
        public T next() {
            if (hasNext()) {
                if (iteratorNotification.isOnNext()) {
                    T v = iteratorNotification.getValue();
                    iteratorNotification = null;
                    return v;
                }
            }
            throw new NoSuchElementException();
        }

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

    }
}

View on GitHub (pinned to a8ab535614)

Solutions

  1. Never call remove() on a latest-buffered iterator; perform filtering upstream with filter() instead.
  2. Materialize the items you want into a mutable ArrayList first, then mutate that list.
  3. Review generic iteration utilities to confirm they skip remove() or are documented as read-only safe.
  4. Switch to a collect-then-process pattern: gather items, then operate on the collected list.

Example fix

// before
Iterator<T> it = latestIterator; // from BlockingFlowableLatest
while (it.hasNext()) {
    if (stale(it.next())) it.remove(); // throws 'Read-only iterator.'
}

// after
List<T> fresh = new ArrayList<>();
flowable.filter(t -> !stale(t)).blockingSubscribe(fresh::add);
Defensive patterns

Strategy: type-guard

Validate before calling

// latest-buffered iterators are read-only; collect then mutate.
List<T> list = new ArrayList<>();
flowable.filter(t -> !stale(t)).blockingSubscribe(list::add);

Type guard

// iterators from BlockingFlowableLatest throw on remove();
// only iterate, never mutate through them.
boolean isReadOnly(Iterator<?> it) {
    return it.getClass().getName().contains("BlockingFlowableLatest");
}

Prevention

When it happens

Trigger: Iterating the result of a 'latest'-mode blocking view (e.g. blockingLatest-style access) and invoking iterator.remove() on it — typically from ported List-mutation code or a generic utility that calls remove().

Common situations: Adapting List-based cleanup loops to a latest-buffered stream; passing the read-only iterator into a helper that probes remove(); refactoring from a mutable buffer to a reactive latest view.

Related errors


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