ReactiveX/RxJava · warning · UnsupportedOperationException

remove

Error message

remove

What it means

Thrown by the remove() method of the Iterator/Disposable produced by Observable.blockingIterable(). Because the iterable is backed by a streaming Observable (items are produced async and buffered in a queue), there is no backing collection to remove from, so remove() throws UnsupportedOperationException("remove"). The comment notes it overrides a default method for Java 7 compatibility.

Source

Thrown at src/main/java/io/reactivex/rxjava4/internal/operators/observable/BlockingObservableIterable.java:152

        @Override
        public void onComplete() {
            done = true;
            signalConsumer();
        }

        void signalConsumer() {
            lock.lock();
            try {
                condition.signalAll();
            } finally {
                lock.unlock();
            }
        }

        @Override // otherwise default method which isn't available in Java 7
        public void remove() {
            throw new UnsupportedOperationException("remove");
        }

        @Override
        public void dispose() {
            DisposableHelper.dispose(this);
            signalConsumer(); // Just in case it is currently blocking in hasNext.
        }

        @Override
        public boolean isDisposed() {
            return DisposableHelper.isDisposed(get());
        }
    }
}

View on GitHub (pinned to a8ab535614)

Solutions

  1. Remove the it.remove() call from your consumption loop; the iterator is read-only.
  2. Collect consumed values into your own list and drop unwanted ones there rather than removing from the iterator.
  3. Filter upstream with Observable.filter(...) before blockingIterable() to exclude items.
  4. If a component requires remove(), delegate through an Iterator whose remove() is a no-op.

Example fix

// before
Iterator<T> it = observable.blockingIterable().iterator();
while (it.hasNext()) { T v = it.next(); if (stale(v)) it.remove(); }
// after
List<T> fresh = new ArrayList<>();
Iterator<T> it = observable.blockingIterable().iterator();
while (it.hasNext()) { T v = it.next(); if (!stale(v)) fresh.add(v); }
Defensive patterns

Strategy: validation

Validate before calling

// Read-only iterator: never call remove().
Iterator<T> it = observable.blockingIterable().iterator();
while (it.hasNext()) {
    T v = it.next();
    // process v; it.remove() is unsupported
}

Prevention

When it happens

Trigger: Calling it.remove() on the iterator from observable.blockingIterable().iterator(); a generic drain utility that invokes remove(); legacy collection code repurposed onto a blocking iterable.

Common situations: Reusing imperative drain code that calls remove() to mark consumption; third-party library that calls remove() on any iterator it owns; forgetting blocking iterables are unmodifiable views.

Related errors


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