none
What are the OnError symantics?

    Question

  • Hi,
      I'm having some inconsitent behavior using Subject.OnError. Sometimes it works fine and other times it throws inside OnError. The difference is if I have a subscriber that consumes err on the other end. Here is some psuedo code:
    Subject<string> s1 = new Subject<string>();
    Subject<string> s2 = new Subject<string>();
    
    s1.Subscribe(a => Console.WriteLine(a), err => {}, () => {});
    
    /// No throw
    s1.OnError (new Exception("boom"));
    
    /// undealt with exception thrown next line:
    s2.OnError (new Exception("boom"));
    Is this proper behavior? Should all IObservables I create behaive this way? It seems odd because in some sense the behavior of the Subject's subscribers.

    Cheers, Gordon.
    Gordon
    Tuesday, January 19, 2010 9:26 AM

Answers

  • Hi Gordon,

    If you try s1.Subscribe(Console.WriteLine), without passing in a value for onError, then s1 behaves like s2.  The point, it seems, is that if any subscribers claim to be able to handle the error then it won't be thrown; if not, then the error is thrown so that it will be seen by the application.  This prevents an error going unnoticed, unless of course you use something like your example: ex => {}, but that's just like doing

    try {
    ...
    }
    catch {}

    to ignore all exceptions.

    I believe this behavior is the same for all IObservables, not just Subject<T> implementations.

    There's a Channel 9 video that discusses this behavior a bit:

    http://channel9.msdn.com/posts/J.Van.Gogh/Reactive-Extensions-API-in-depth-Subscribing/ 

    - Dave
    http://davesexton.com/blog
    • Marked as answer by GordonTWatts Tuesday, January 19, 2010 7:13 PM
    Tuesday, January 19, 2010 3:12 PM

All replies

  • Hi Gordon,

    If you try s1.Subscribe(Console.WriteLine), without passing in a value for onError, then s1 behaves like s2.  The point, it seems, is that if any subscribers claim to be able to handle the error then it won't be thrown; if not, then the error is thrown so that it will be seen by the application.  This prevents an error going unnoticed, unless of course you use something like your example: ex => {}, but that's just like doing

    try {
    ...
    }
    catch {}

    to ignore all exceptions.

    I believe this behavior is the same for all IObservables, not just Subject<T> implementations.

    There's a Channel 9 video that discusses this behavior a bit:

    http://channel9.msdn.com/posts/J.Van.Gogh/Reactive-Extensions-API-in-depth-Subscribing/ 

    - Dave
    http://davesexton.com/blog
    • Marked as answer by GordonTWatts Tuesday, January 19, 2010 7:13 PM
    Tuesday, January 19, 2010 3:12 PM
  • Ah, thanks! Looking at it from that POV makes more sense. And I'd forgotten about the video.
    Gordon
    Tuesday, January 19, 2010 7:14 PM
  • The point, it seems, is that if any subscribers claim to be able to handle the error then it won't be thrown; 

    hm ... I thought if one of the Subscribe() extension methods which do not have a onError argument is used they will throw the exception when OnError() is called. This means even if other subscribers handle the error it will be thrown by the subscriber which has no OnError handler...

    At least that is what I would expect from viewing at the Subscribe() methods with Reflector...

    Thursday, January 21, 2010 9:26 AM
  • Hi Andreas,

    Yes, you're correct.  For hot observables with multiple subscribers, all subscribers must supply an onError function otherwise the error will be thrown.

    var s1 = new Subject<string>();

    s1.Subscribe(Console.WriteLine, ex => Console.WriteLine(ex.Message));
    s1.Subscribe(Console.WriteLine); //, ex => Console.WriteLine(ex.Message));

    // throws if the second subscription does not supply onError
    s1.OnError(new Exception("First exception"));


    - Dave


    http://davesexton.com/blog
    Thursday, January 21, 2010 2:59 PM
  • Hey, are you guys getting the same error as me for the following code?  I was going to show how onError behavior differed in cold observables, but to my surprise I was able to repro the Task error that I got in some other discussion.

    Here's the code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ReactiveProgrammingConsole
    {
      class Program
      {
        static void Main()
        {
          var s2 = Numbers().ToObservable();
    
          using (s2.Subscribe(Console.WriteLine, ex => Console.WriteLine(ex.Message)))
          using (s2.Subscribe(Console.WriteLine)) //, ex => Console.WriteLine(ex.Message))
          {
            Console.ReadLine();
          }
        }
    
        static IEnumerable<int> Numbers()
        {
          yield return 1;
          yield return 2;
          throw new Exception("Error on 3.");
        }
      }
    }
    


    And here's the error, which occurs when the application shuts down by pressing a key.

    System.AggregateException was unhandled
      Message=A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.
      Source=mscorlib
      StackTrace:
           at System.Threading.Tasks.TaskExceptionHolder.Finalize()
      InnerException:
           Message=Error on 3.
           Source=System.Reactive
           StackTrace:
             Server stack trace:
                at ReactiveProgrammingConsole.ErrorTest.<Numbers>d__2.MoveNext() in F:\Projects\_Lab\ReactiveProgramming\ReactiveProgrammingConsole\ErrorTest.cs:line 26
                at System.Linq.Observable.<>c__DisplayClass1de`1.<Subscribe>b__1dd(Action self)
             Exception rethrown at [0]:
                at System.ObservableExtensions.<Subscribe>b__3[TSource](Exception exception)
                at System.Collections.Generic.AnonymousObserver`1.Error(Exception exception)
                at System.Collections.Generic.AbstractObserver`1.OnError(Exception exception)
                at System.Collections.Generic.AnonymousObservable`1.AutoDetachObserver.Error(Exception exception)
                at System.Collections.Generic.AbstractObserver`1.OnError(Exception exception)
                at System.Linq.Observable.<>c__DisplayClass1de`1.<Subscribe>b__1dd(Action self)
                at System.Threading.Scheduler.<>c__DisplayClass5.<Schedule>b__2()
                at System.Threading.Scheduler.<>c__DisplayClass5.<>c__DisplayClass7.<Schedule>b__4()
                at System.Threading.Tasks.Task.InnerInvoke()
                at System.Threading.Tasks.Task.Execute()

    - Dave
    http://davesexton.com/blog
    Thursday, January 21, 2010 3:18 PM
  • I don't get the System.AggregateException (3.5 bits). 

    Update:  I do get it with 4.0 bits.

    • Edited by Richard Hein Friday, January 22, 2010 6:33 PM update 4.0 test
    Friday, January 22, 2010 6:25 PM