Event interrupt when exception is thrown

Sorry if this has been answered but I couldn’t find post about it. I’m having a problem when exception is thrown in on of methods assigned to event. I’m actively using observer pattern and I have a GameManager that fires event if game state is changed. I noticed the execution doesn’t perform correctly when there had been thrown exception in one of assigned methods. So I want to know is there a way to prevent event from interrupting it’s methods execution even if exceptions are thrown, I know I should fix exceptions but still is it possible to keep event working ?

To be fair, if code gets a compiler error, you can’t judge anything until that error is fixed… words of advice!

Is it compilation error? If it was I wouldn’t be able to even run the game. It’s the runtime error, isn’t it?

Negative, once one set of the code stops thinking because of an error, it won’t run anything past that. Especially if that determinate point sets the bar for something else. So like I said, always fix the first error in line, all else should be easy after that point. :slight_smile:

I’m always able to keep running different parts of my projects when a small section fails, as far as I know, the code tries to always protect itself, but it does the best it can with user error(run-away for loops and what-not)…

Just imagine it’s all a road map to the end goal, and errors are road blocks, how many road blocks do you think are acceptable? I say none, and I feel the compiler feels the same…

Now however, you could have two un-related issues, and are thinking they’re the same… But how would you ever know, until you rid yourself of all other errors?

Methods generally exit execution if an error is thrown, either unintentionally or intentionally (as in when you throw new System.Exception();, for example).

What exception are you getting? Can you guard your logic against it?

It was just a NullRef exception, it was just interesting to know if there is a way to protect event from this because it felt strange that all execution fails if one method throws exception but after this explanation

it feels ok

Well a null ref is one of the most common error you will encounter, and your code should be built to handle null values, or should not be encountering nulls in the first place: https://forum.unity.com/threads/how-to-fix-a-nullreferenceexception-error.1230297/

It can be as simple as just checking if (something == null) and logging a warning, then executing the logic early with return;.

Depends where the null-ref is happening of course. Your first course of action should of couse be to investigate the cause and prevent it from happening.

To answer the question, exception handling is done with try catch:

try {}
catch(Exception ex) {}

My personal solution to this issue was to create my own event implementation. Then you can invoke the event for each observer in a loop with a Try / Catch, capture the single exception, or compile multiple exceptions into a list if you go beyond one exception, then rethrow the original captured exception, or throw a new AggregateException with the multiple exceptions nested as inner exceptions. That’s how my personal implementation of an update manager works, so when using that to update instead of Update(), if 100 objects are updating, and 50 of them randomly throw an exception, the other 50 all still update, and all of the rest of the logic in the frame is still executed. The exception(s) are caught and logged immediately after the event is invoked by the update manager that is using my custom event type for the update.

When you call your event you can wrap the event call in a try/catch:

public event System.Action SomeEvent;

void SomeCode()
{
    //doing stuff

    //calling event
    try
    {
        SomeEvent?.Invoke();
    }
    catch (System.Exception ex)
    {
        Debug.LogException(ex);
    }

    //doing rest of stuff uninterrupted by exception in SomeEvent callbacks
}

Note though that if one of the callbacks/handlers in SomeEvent throws an exception, not all handlers/callbacks registered will complete. So say 3 handlers are registered, 1st completes, 2nd throws, 3rd never gets reached.

Of course, you could technically call ‘GetInvocationList’ on the event delegate to enumerate the individual handlers/callbacks:

Something like:

public event System.Action SomeEvent;

void SomeCode()
{
    //doing stuff

    //calling event
    foreach (var d in SomeEvent.GetInvocationList())
    {
        try
        {
            (d as System.Action).Invoke();
        }
        catch (System.Exception ex)
        {
            Debug.LogException(ex);
        }
    }

    //doing rest of stuff uninterrupted by exception in SomeEvent callbacks
}

And that could even be wrapped into its own extension method for ease of reuses:

    static class DelegateExtensions
    {

        public static void InvokeSafely<T>(this T del, params object[] args) where T : System.Delegate
        {
            if (del == null) return;

            foreach (var d in del.GetInvocationList())
            {
                try
                {
                    (d as T).DynamicInvoke();
                }
                catch (System.Exception ex)
                {
                    Debug.LogException(ex);
                }
            }
        }

    }

(note the implications of creating collections in the moment and dynamically invoking… this is garbage heavy and slower than just calling the event directly)

But I mean, at this point… wtf are we even doing? Fix your exception! This isn’t how events should work.

And if for whatever reason you need to allow the exception in your specific event handler. Wrap that with a try/catch in place.

void MyEventHandler()
{
    try
    {
         //do stuff
    }
    catch {} //so my exception doesn't bubble out
}

This is the true answer, of course. Exceptions are unacceptable!

I didn’t create my own event class for the ability to continue invoking observers in the face of individual exceptions, I had other more important reasons that served as the original motivation, it’s just something extra that I added while I was in there. It should only be relevant in the situation where I’m encountering an exception, which shouldn’t be happening and is going to be fixed right now, today!