I want to share my experience with the UndeliverableException which i haven’t noticed before. To be honest it’s my shame to hasn’t been check public documentation for a while :(

TLDR;

We need to handle RxJavaPlugins.setErrorHandler() even while handling onError() function for ObservableSource, SingleSource …

Today I started to got an error from Firebase Testlab due to UndeliverableException like below



com.testapp: Waiting for the face detection model to be downloaded. Please wait. io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling com.testapp: Waiting for the face detection model to be downloaded. Please wait. at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)

The error was obvious but I couldn’t figure out the problem quickly because each time when i rerun test flow an exception was appearing on the other test and I was thinking “I’ve already handled errors” on handleException() function like below

observable.subscribe({

TODO()

}, {

handleException(it)

}, {

TODO()

})

But after visiting the error handling documentation penny dropped. Documentation tells throwable errors doesn’t swallowed after the observable disposed/terminated

What does it mean ?

In your stream if an error occur after your stream terminated or disposed you can still get an exception. But how ?, let me show it in simple example

Observable.create<String> {

it.onNext("test")

it.onComplete()

}.subscribe({

println(it)

}, {

println(it)

}, {

println("onCompleted")

})

It prints “test” and “onCompleted” as expected. But what will happen if we call onError() after we called onComplete() function ?

Observable.create<String> {

it.onNext("test")

it.onComplete()

it.onError(IOException()) // added this line

}.subscribe({

println(it)

}, {

println(it)

}, {

println("onCompleted")

})

UndeliverableException thrown 🎉🎉🎉🎉

So far so good, let jump into CreateEmitter source code to understand why it happened.

static final class CreateEmitter<T>

extends AtomicReference<Disposable>

implements ObservableEmitter<T>, Disposable { ... @Override

public void onError(Throwable t) {

if (!tryOnError(t)) {

RxJavaPlugins.onError(t);

}

}

tryOnError implementation

@Override

public boolean tryOnError(Throwable t) {

if (t == null) {

t = new NullPointerException("onError called with null. Null values are generally not allowed in 2.x operators and sources.");

}

if (!isDisposed()) {

try {

observer.onError(t);

} finally {

dispose();

}

return true;

}

return false;

}

So in tryOnError() function try to call observer’s onError() function if it is not disposed. On the other case it calls RxJavaPlugins.onError which causes thrown UndeliverableException.

Before share cure I want also show the implementation of RxJavaPlugins.onError function

RxJavaPlugins.onError()

So if an error occur after the emitter disposed onError() function will be called and if the throwable is not bug the throwable wrapped with UndeliverableException and called current thread’s uncaughtException() function.

PS: isBug() function returns true for the below exception types;

OnErrorNotImplementedException, MissingBackpressureException, IllegalStateException, NullPointerException, IllegalArgumentException, CompositeException

How should we handle this problem ?

We need to override RxJavaPlugins.setErrorHandler() function to handle this kind of exception.

RxJavaPlugins.setErrorHandler {

TODO("handle exception")

}

If you don’t want to handle and ignore it you can also use emptyConsumer() function which is provided by the library.

By library’s documentation you can handle error like below

Reference:

- https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling