ReactiveX / RxJava

RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.
Apache License 2.0
47.88k stars 7.61k forks source link

2.1.0 - Question - Cause of io.reactivex.exceptions.CompositeException #5363

Closed jmitchell38488 closed 7 years ago

jmitchell38488 commented 7 years ago

Version: 2.1.0 with Android

    compile 'io.reactivex.rxjava2:rxjava:2.1.0'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

There's probably better ways of doing what I'm trying to do, but I've come across a crash report in my application: io.reactivex.exceptions.CompositeException

This is the stack trace:

io.reactivex.exceptions.CompositeException: 
  at io.reactivex.internal.observers.LambdaObserver.onError(LambdaObserver.java:77)
  at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.checkTerminated(ObservableObserveOn.java:276)
  at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:172)
  at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252)
  at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
  at android.os.Handler.handleCallback(Handler.java:751)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:154)
  at android.app.ActivityThread.main(ActivityThread.java:6688)
  at java.lang.reflect.Method.invoke(Native Method:0)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)

What I'm doing is triggering a background sync task. There's a PublishSubject<T> that provides an Observable<T> with .hide() and when the content is downloaded from the internet, and stored in the database (all asynchronous tasks), the PublishSubject<T> is sent a notification with .onNext().

This is observed by another activity that handles some other logic.

I've declared a Consumer<T> and assigned the Observable<T> from the PublishSubject<T> to a Disposable<T> object. When the PublishSubject<T> is triggered with .onNext() the Consumer<T> does its work, then calls:

if (mDisposableObs != null && !mDisposableObs.isDisposed()) {
    mDisposableObs.dispose();
}

This is done inside the Consumer<T>. I intentionally delay the receipt of notifications from the PublishSubject<T> for network connectivity problems, delays etc.

// simplified for readability
mDisposableObs = MyPublishSubject
                .hide()
                .delay(8000, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(myConsumer);

The exception appears to be thrown during the drainNormal operation inside the ObservableObserveOn class:

if (checkTerminated(done, q.isEmpty(), a)) {
    return;
}

In turn thrown by:

if (e != null) {
    queue.clear();
    a.onError(e); // Throwable e = error; <--- how to identify without the root cause?
    worker.dispose();
    return true;
} else
if (empty) {
    a.onComplete();
    worker.dispose();
    return true;
}

I do have instances of CompositeDisposable in my application but only at UI level. The crash happened when the application was in the background.

Thoughts?

JakeWharton commented 7 years ago

Include the full trace including the causes.

On Tue, May 23, 2017, 7:41 PM Justin notifications@github.com wrote:

Version: 2.1.0 with Android

compile 'io.reactivex.rxjava2:rxjava:2.1.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

There's probably better ways of doing what I'm trying to do, but I've come across a crash report in my application: io.reactivex.exceptions.CompositeException

This is the stack trace:

io.reactivex.exceptions.CompositeException: at io.reactivex.internal.observers.LambdaObserver.onError(LambdaObserver.java:77) at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.checkTerminated(ObservableObserveOn.java:276) at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:172) at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252) at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6688) at java.lang.reflect.Method.invoke(Native Method:0) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)

What I'm doing is triggering a background sync task. There's a PublishSubject that provides an Observable with .hide() and when the content is downloaded from the internet, and stored in the database (all asynchronous tasks), the PublishSubject is sent a notification with .onNext().

This is observed by another activity that handles some other logic.

I've declared a Consumer and assigned the Observable from the PublishSubject to a Disposable object. When the PublishSubject is triggered with .onNext() the Consumer does its work, then calls:

if (mDisposableObs != null && !mDisposableObs.isDisposed()) { mDisposableObs.dispose(); }

This is done inside the Consumer. I intentionally delay the receipt of notifications from the PublishSubject for network connectivity problems, delays etc.

// simplified for readability mDisposableObs = MyPublishSubject .hide() .delay(8000, TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribe(myConsumer);

The exception appears to be thrown during the drainNormal operation inside the ObservableObserveOn class:

if (checkTerminated(done, q.isEmpty(), a)) { return; }

In turn thrown by:

if (e != null) { queue.clear(); a.onError(e); // Throwable e = error; <--- how to identify without the root cause? worker.dispose(); return true; } elseif (empty) { a.onComplete(); worker.dispose(); return true; }

Thoughts?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxJava/issues/5363, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEEEa3zOOYFQdZZEFwaPU3eatpzYN6aks5r826YgaJpZM4NkbK7 .

jmitchell38488 commented 7 years ago

Hi Jake, that's the full stack trace from the crash report. It's a production app, and the crash happened on my phone while it was most likely background synchronising.

I'll see if I can get more info.

jmitchell38488 commented 7 years ago

@JakeWharton Can't get anymore than the stack trace already posted. Under what circumstances would that exception be thrown?

The app was in the background when it crashed, so it could have been caused by the alarm manager starting the app for a background sync, or a thread in UI crashing after it had been put to sleep.

JakeWharton commented 7 years ago

Your onError handler is throwing an exception. All CompositeExceptions should have at least two 'caused by' traces as we can't know the original error here that triggered it.

On Tue, May 23, 2017 at 7:41 PM Justin notifications@github.com wrote:

Version: 2.1.0 with Android

compile 'io.reactivex.rxjava2:rxjava:2.1.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

There's probably better ways of doing what I'm trying to do, but I've come across a crash report in my application: io.reactivex.exceptions.CompositeException

This is the stack trace:

io.reactivex.exceptions.CompositeException: at io.reactivex.internal.observers.LambdaObserver.onError(LambdaObserver.java:77) at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.checkTerminated(ObservableObserveOn.java:276) at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:172) at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252) at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6688) at java.lang.reflect.Method.invoke(Native Method:0) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)

What I'm doing is triggering a background sync task. There's a PublishSubject that provides an Observable with .hide() and when the content is downloaded from the internet, and stored in the database (all asynchronous tasks), the PublishSubject is sent a notification with .onNext().

This is observed by another activity that handles some other logic.

I've declared a Consumer and assigned the Observable from the PublishSubject to a Disposable object. When the PublishSubject is triggered with .onNext() the Consumer does its work, then calls:

if (mDisposableObs != null && !mDisposableObs.isDisposed()) { mDisposableObs.dispose(); }

This is done inside the Consumer. I intentionally delay the receipt of notifications from the PublishSubject for network connectivity problems, delays etc.

// simplified for readability mDisposableObs = MyPublishSubject .hide() .delay(8000, TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribe(myConsumer);

The exception appears to be thrown during the drainNormal operation inside the ObservableObserveOn class:

if (checkTerminated(done, q.isEmpty(), a)) { return; }

In turn thrown by:

if (e != null) { queue.clear(); a.onError(e); // Throwable e = error; <--- how to identify without the root cause? worker.dispose(); return true; } elseif (empty) { a.onComplete(); worker.dispose(); return true; }

Thoughts?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxJava/issues/5363, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEEEa3zOOYFQdZZEFwaPU3eatpzYN6aks5r826YgaJpZM4NkbK7 .

jmitchell38488 commented 7 years ago

@JakeWharton thanks for following up - I guess it wasn't on my phone and the crash looks like it was for another app, just thought coincidence based on the time.

That's as much information as I have. I'll see if any of my onError handlers are throwing. Is that the only cause, or are there other causes?

Thanks

exlaine commented 7 years ago

Hey @jmitchell38488 did you solve this problem now? I'm now having the same issue, and seems like it cached a previous emitted error.

akarnokd commented 7 years ago

Looks like the onError Consumer of a subscribe() crashed in response to an upstream error and now the global handler is receiving a composite of the original and handler crashes but somehow the original errors are missing. If this is from a log, it might be possible the stacktrace was interleaved with other log activity and the rest of the error report is somewhere else.

jmitchell38488 commented 7 years ago

@JakeWharton was right - I was using a different work flow and didn't remove the re-throws that were inside the catch block. The error was being caused by a network error being throw. As a new throwable to the global stack, instead of sending an error to the subscribers.

Shit code on my behalf caused this error.

jmitchell38488 commented 7 years ago

@exlaine yes, see my answer above.