ReactiveX/RxJava · warning · UnsupportedOperationException
Read only iterator
Error message
Read only iterator
What it means
Thrown by Iterator.remove() on the iterator from Observable.blockingNext(). The iterator yields Notifications pulled one at a time from a push source and is permanently read-only, so remove() throws UnsupportedOperationException("Read only iterator"). No backing collection exists to remove from.
Source
Thrown at src/main/java/io/reactivex/rxjava4/internal/operators/observable/BlockingObservableNext.java:128
@Override
public T next() {
if (error != null) {
// If any error has already been thrown, throw it again.
throw ExceptionHelper.wrapOrThrow(error);
}
if (hasNext()) {
isNextConsumed = true;
return next;
}
else {
throw new NoSuchElementException("No more elements");
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("Read only iterator");
}
}
static final class NextObserver<T> extends DisposableObserver<Notification<T>> {
private final BlockingQueue<Notification<T>> buf = new ArrayBlockingQueue<>(1);
final AtomicInteger waiting = new AtomicInteger();
@Override
public void onComplete() {
// ignore
}
@Override
public void onError(Throwable e) {
RxJavaPlugins.onError(e);
}
@OverrideView on GitHub (pinned to a8ab535614)
Solutions
- Delete the it.remove() call; the iterator is read-only.
- Filter upstream with Observable.filter(...) to drop items before blocking iteration.
- Collect retained values into your own list rather than mutating the iterator.
- Wrap with a forwarding Iterator whose remove() is a no-op if a component requires remove().
Example fix
// before
Iterator<T> it = observable.blockingNext().iterator();
while (it.hasNext()) { T v = it.next(); if (drop(v)) it.remove(); }
// after
Iterator<T> it = observable.blockingNext().iterator();
while (it.hasNext()) { T v = it.next(); if (!drop(v)) { /* keep v */ } } Defensive patterns
Strategy: validation
Validate before calling
// Read-only iterator: never call remove().
Iterator<T> it = observable.blockingNext().iterator();
while (it.hasNext()) {
T v = it.next();
// process v; it.remove() is unsupported
} Prevention
- Recognize blockingNext() iterators as read-only views of a push source.
- Filter upstream (Observable.filter) instead of removing downstream.
- Forward through a no-op remove() wrapper if a dependency calls remove().
- Keep consumed items in your own mutable list rather than mutating the iterator.
When it happens
Trigger: Calling it.remove() on the iterator from observable.blockingNext().iterator(); a generic drain routine that calls remove(); reusing imperative collection code on a blocking iterator.
Common situations: Ported List-walking code that paired next() with remove(); a dependency that calls remove() to drain; forgetting blocking iterators are views over a stream, not collections.
Related errors
AI-assisted analysis of ReactiveX/RxJava@a8ab535614 (2026-08-13).
Data as JSON: /api/errors/8b792d55f3d93ea6.
Report an issue: GitHub.