{"record":{"id":"8b792d55f3d93ea6","repo":"ReactiveX/RxJava","slug":"read-only-iterator-8b792d","errorCode":null,"errorMessage":"Read only iterator","messagePattern":"Read only iterator","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"warning","filePath":"src/main/java/io/reactivex/rxjava4/internal/operators/observable/BlockingObservableNext.java","lineNumber":128,"sourceCode":"\n        @Override\n        public T next() {\n            if (error != null) {\n                // If any error has already been thrown, throw it again.\n                throw ExceptionHelper.wrapOrThrow(error);\n            }\n            if (hasNext()) {\n                isNextConsumed = true;\n                return next;\n            }\n            else {\n                throw new NoSuchElementException(\"No more elements\");\n            }\n        }\n\n        @Override\n        public void remove() {\n            throw new UnsupportedOperationException(\"Read only iterator\");\n        }\n    }\n\n    static final class NextObserver<T> extends DisposableObserver<Notification<T>> {\n        private final BlockingQueue<Notification<T>> buf = new ArrayBlockingQueue<>(1);\n        final AtomicInteger waiting = new AtomicInteger();\n\n        @Override\n        public void onComplete() {\n            // ignore\n        }\n\n        @Override\n        public void onError(Throwable e) {\n            RxJavaPlugins.onError(e);\n        }\n\n        @Override","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/ReactiveX/RxJava/blob/a8ab5356143f37a8f1dd6d76c79191bec5fa343b/src/main/java/io/reactivex/rxjava4/internal/operators/observable/BlockingObservableNext.java#L110-L146","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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()."],"exampleFix":"// before\nIterator<T> it = observable.blockingNext().iterator();\nwhile (it.hasNext()) { T v = it.next(); if (drop(v)) it.remove(); }\n// after\nIterator<T> it = observable.blockingNext().iterator();\nwhile (it.hasNext()) { T v = it.next(); if (!drop(v)) { /* keep v */ } }","handlingStrategy":"validation","validationCode":"// Read-only iterator: never call remove().\nIterator<T> it = observable.blockingNext().iterator();\nwhile (it.hasNext()) {\n    T v = it.next();\n    // process v; it.remove() is unsupported\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["rxjava","iterator","unsupported-operation","blocking","observable"],"backgroundTag":null,"analyzedSha":"a8ab5356143f37a8f1dd6d76c79191bec5fa343b","analyzedAt":"2026-08-13T23:25:30.069Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}