ReactiveX/RxJava · warning · UnsupportedOperationException
Read-only iterator.
Error message
Read-only iterator.
What it means
Thrown by Iterator.remove() on the iterator returned from Observable.blockingLatest(). The iterator surfaces the most recent Notification from a push source and is read-only by contract, so remove() always throws UnsupportedOperationException("Read-only iterator."). There is no mutable backing collection to alter.
Source
Thrown at src/main/java/io/reactivex/rxjava4/internal/operators/observable/BlockingObservableLatest.java:111
throw ExceptionHelper.wrapOrThrow(n.getError());
}
}
return iteratorNotification.isOnNext();
}
@Override
public T next() {
if (hasNext()) {
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
- Drop the it.remove() call; blockingLatest() iterators are read-only.
- Filter upstream with Observable.filter(...) to remove unwanted items before blocking iteration.
- Accumulate the items you want to keep in a separate list rather than mutating the iterator.
- Wrap with a forwarding Iterator whose remove() is a no-op if a dependency mandates remove().
Example fix
// before
Iterator<T> it = observable.blockingLatest().iterator();
while (it.hasNext()) { T v = it.next(); if (skip(v)) it.remove(); }
// after
Iterator<T> it = observable.blockingLatest().iterator();
while (it.hasNext()) { T v = it.next(); if (!skip(v)) { /* keep v */ } } Defensive patterns
Strategy: validation
Validate before calling
// Read-only iterator: never call remove().
Iterator<T> it = observable.blockingLatest().iterator();
while (it.hasNext()) {
T v = it.next();
// process v; it.remove() is unsupported
} Prevention
- Recognize blockingLatest() iterators as read-only.
- Filter upstream with Observable.filter(...) instead of removing.
- Keep a separate mutable list of retained items.
- Forward through a no-op remove() wrapper if a dependency calls remove().
When it happens
Trigger: Calling it.remove() on the iterator from observable.blockingLatest().iterator(); a generic cleanup routine invoking remove(); reusing collection-based drain code.
Common situations: Imperative drain loops ported from List iteration that included remove(); libraries that call remove() on consumed iterators; misunderstanding blockingLatest() as a mutable buffer.
Related errors
AI-assisted analysis of ReactiveX/RxJava@a8ab535614 (2026-08-13).
Data as JSON: /api/errors/69328b9145b0499d.
Report an issue: GitHub.