关于RxJava2中UndeliverableException与已销毁流的疑问
Hey Tony, let’s break down these RxJava 2 error handling gotchas you’re stuck on—since you already saw the take() throwing UndeliverableException behavior, let’s dig into why that happens first, then cover better handling patterns, and touch on the disposed Single issue too.
Why does take() throw UndeliverableException?
The core issue here is RxJava 2’s strict rule: every error emitted by an Observable must have an active subscriber to receive it. When you use take(n), once the Observable emits n items, it automatically disposes the downstream subscription. If the upstream Observable keeps running and later throws an error, there’s no longer a subscriber to pass that error to—so RxJava throws an UndeliverableException because it has nowhere to deliver the error.
Here’s a concrete example that replicates this:
Observable.interval(100, TimeUnit.MILLISECONDS) .take(2) // After 2 items, the subscription is disposed .map(v -> { // When v reaches 3, the subscription is already dead if (v == 3) throw new RuntimeException("Oops, late error!"); return v; }) .subscribe( System.out::println, e -> System.err.println("Caught error: " + e) // This won't catch the v=3 error );
By the time v=3 rolls around, take(2) has already disposed the subscription. The error can’t reach the subscriber’s error handler, so RxJava surfaces it as an UndeliverableException.
Better ways to handle this
Let’s go from best to fallback solutions:
Clean up upstream sources on dispose (the ideal fix):
If you control the upstream Observable, make sure you stop emitting events (and avoid errors) once the subscription is disposed. UsedoOnDispose()to terminate any ongoing work:PublishSubject<Long> subject = PublishSubject.create(); Disposable disposable = subject .take(2) .subscribe(System.out::println, e -> System.err.println("Caught: " + e)); subject.onNext(1L); subject.onNext(2L); disposable.dispose(); // In a real scenario, use doOnDispose to shut down the subject's event source subject.doOnDispose(() -> { // Stop generating new events here to prevent late errors });For custom Observables/Singles, always tie your asynchronous work to the emitter’s disposable—this ensures work is canceled when the subscription is disposed:
Single.create(emitter -> { Disposable networkCall = myApi.fetchData() .subscribe(result -> emitter.onSuccess(result), emitter::onError); emitter.setDisposable(networkCall); // Cancels the call if the Single is disposed });Use a global error handler as a safety net:
If you can’t control the upstream (e.g., third-party libraries), set a global RxJava error handler to catchUndeliverableExceptionand prevent app crashes. This is a fallback, not a primary fix:RxJavaPlugins.setErrorHandler(e -> { if (e instanceof UndeliverableException) { e = e.getCause(); // Unwrap the actual error } // Log the error instead of letting it crash the app System.err.println("Undeliverable error caught: " + e.getMessage()); });Avoid late emissions with
takeUntil()ordisposeOnComplete():
For some cases, you can usetakeUntil()to explicitly stop the upstream when the downstream completes, instead of relying ontake(n)’s automatic dispose.
What about uncaught RuntimeExceptions in disposed Singles?
This is the same root problem as UndeliverableException—just specific to Singles. When a Single is disposed (e.g., the subscriber cancels the subscription), but the underlying task later throws a RuntimeException, there’s no active subscriber to receive the error. RxJava can’t deliver it, so it becomes an uncaught exception.
The fixes mirror the Observable scenario:
- Tie your Single’s work to the emitter’s disposable to cancel work on dispose.
- Use the global RxJava error handler to catch any straggler exceptions.
Key Takeaways
- RxJava 2 enforces error delivery—no active subscriber = no way to handle the error, which triggers
UndeliverableExceptionor uncaught RuntimeExceptions. - Prevent errors at the source by cleaning up upstream work when subscriptions are disposed—this is the most reliable solution.
- Global error handlers are a safety net, not a replacement for proper cleanup.
内容的提问来源于stack exchange,提问作者TonyR

