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
- Never call remove() on a latest-buffered iterator; perform filtering upstream with filter() instead.
- Materialize the items you want into a mutable ArrayList first, then mutate that list.
- Review generic iteration utilities to confirm they skip remove() or are documented as read-only safe.
- 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
- Do not call remove() on latest-buffered iterators.
- Filter upstream with filter() instead of mutating during iteration.
- Keep read-only iterators away from generic mutation utilities.
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.