Search code examples
kotlinrx-javarx-java2

RxJava UndeliverableException how to handle onSuccess consumer exception?


The exception ended up not handled and was reported as a non-fatal in Crashlytics, hence fell off our radar User saw a blank screen which is not desirable A crash would have been better, for fail-fast is preferred

Ideally, I'd like to the onError consumer to be trigger, that's where I handle the error e.g. shows error UI for every stream

However,

fun main() {
    Single.just(listOf("efaewf"))
            .subscribe({
                println("result is ${it[1]}")
            }, {
                println("handling exception")
                it.printStackTrace()
            })
}

shows that onError consumer is not working

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 | java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
    at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
    at io.reactivex.internal.observers.ConsumerSingleObserver.onSuccess(ConsumerSingleObserver.java:65)
    at io.reactivex.internal.operators.single.SingleJust.subscribeActual(SingleJust.java:30)
    at io.reactivex.Single.subscribe(Single.java:3603)
    at io.reactivex.Single.subscribe(Single.java:3589)
    at com.coffeemeetsbagel.DummyKt.main(Dummy.kt:7)
    at com.coffeemeetsbagel.DummyKt.main(Dummy.kt)
Caused by: java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
    at java.util.Collections$SingletonList.get(Collections.java:4817)
    at com.coffeemeetsbagel.DummyKt$main$1.accept(Dummy.kt:8)
    at com.coffeemeetsbagel.DummyKt$main$1.accept(Dummy.kt)
    at io.reactivex.internal.observers.ConsumerSingleObserver.onSuccess(ConsumerSingleObserver.java:62)
    ... 5 more
Exception in thread "main" 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 | java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
    at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
    at io.reactivex.internal.observers.ConsumerSingleObserver.onSuccess(ConsumerSingleObserver.java:65)
    at io.reactivex.internal.operators.single.SingleJust.subscribeActual(SingleJust.java:30)
    at io.reactivex.Single.subscribe(Single.java:3603)
    at io.reactivex.Single.subscribe(Single.java:3589)
    at com.coffeemeetsbagel.DummyKt.main(Dummy.kt:7)
    at com.coffeemeetsbagel.DummyKt.main(Dummy.kt)
Caused by: java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
    at java.util.Collections$SingletonList.get(Collections.java:4817)
    at com.coffeemeetsbagel.DummyKt$main$1.accept(Dummy.kt:8)
    at com.coffeemeetsbagel.DummyKt$main$1.accept(Dummy.kt)
    at io.reactivex.internal.observers.ConsumerSingleObserver.onSuccess(ConsumerSingleObserver.java:62)
    ... 5 more

I've read the docs here but don't comprehend fully

I could totally add if {} else {} or try {} catch {} to onSuccess consumer but it feels like code smell

Am I wrong, is if else/ try catch the best and/ or intended way to handle exceptions in onSuccess?


Solution

  • You are using Single which has the protocol onSuccess | onError. So if onSuccess crashes, it can't invoke onError on the same observer.

    In contrast, Observable has the protocol onNext* (onError|onComplete)? so a crashing onNext is then allowed to invoke onError.

    You have a few options with Single:

    • convert it to Observable before the subscribe,
    • add try-catch in the unreliable onSuccess handler,
    • do mapping/transformation in an earlier operator, such as map, so the end consumer is unlikely to crash.