ReactiveX/RxJava · error · UnsupportedOperationException
remove
Error message
remove
What it means
Thrown as UnsupportedOperationException('remove') by the Iterator returned by BlockingFlowableIterable when a caller invokes remove(). The iterator exposes a pull-based blocking view over a push-based flowable; mutation operations are not supported, so remove() always throws. This satisfies the java.util.Iterator contract (remove is optional).
Source
Thrown at src/main/java/io/reactivex/rxjava4/internal/operators/flowable/BlockingFlowableIterable.java:184
void signalConsumer() {
lock.lock();
try {
condition.signalAll();
} finally {
lock.unlock();
}
}
@Override
public void run() {
SubscriptionHelper.cancel(this);
signalConsumer();
}
@Override // otherwise default method which isn't available in Java 7
public void remove() {
throw new UnsupportedOperationException("remove");
}
@Override
public void dispose() {
SubscriptionHelper.cancel(this);
signalConsumer(); // Just in case it is currently blocking in hasNext.
}
@Override
public boolean isDisposed() {
return get() == SubscriptionHelper.CANCELLED;
}
}
}
View on GitHub (pinned to a8ab535614)
Solutions
- Do not call remove() on iterators derived from a flowable; collect into a modifiable collection first if you need to mutate.
- If you need filtered elements, apply filter() on the flowable before iterating instead of removing during iteration.
- Collect into a new ArrayList and mutate that list: flowable.blockingSubscribe(list::add) or blockingStream().toList().
- Audit shared iteration utilities to ensure they do not invoke remove() on the supplied iterator.
Example fix
// before
Iterator<T> it = flowable.blockingIterable().iterator();
while (it.hasNext()) {
T t = it.next();
if (shouldDrop(t)) it.remove(); // throws
}
// after
List<T> kept = new ArrayList<>();
flowable.filter(t -> !shouldDrop(t)).blockingSubscribe(kept::add); Defensive patterns
Strategy: type-guard
Validate before calling
// treat the iterator as read-only by contract; never call remove(). // If mutation is needed, collect first: List<T> list = new ArrayList<>(); flowable.blockingSubscribe(list::add); list.removeIf(this::shouldDrop);
Type guard
// iterators from BlockingFlowableIterable are unmodifiable; route mutation
// through a collected ArrayList instead of iterator.remove().
boolean isReadOnly(Iterator<?> it) {
return it.getClass().getName().contains("BlockingFlowable");
} Prevention
- Never call remove() on iterators obtained from a flowable's blockingIterable().
- Do filtering upstream with filter() rather than removing during iteration.
- Audit generic collection utilities for unconditional remove() calls.
When it happens
Trigger: Obtaining an Iterator via blockingIterable()/forEach iteration over a flowable and calling iterator.remove() on it — e.g. inside a loop that was adapted from a mutable List pattern.
Common situations: Refactoring code that previously iterated and mutated a List (where remove() worked) to stream over a flowable; passing the iterator to a utility that calls remove() (some filtering/cleaning helpers do); generic collection-handling code that probes remove().
Related errors
AI-assisted analysis of ReactiveX/RxJava@a8ab535614 (2026-08-13).
Data as JSON: /api/errors/219c7092e3044a1f.
Report an issue: GitHub.