Log Exceptions from C# Tasks to Unity Console

No Such Dev
3 min readJan 21, 2021

Unity prefers using Coroutines for asynchronous code. However there are times when you have to work with C# Tasks. In my case I am using Google’s Firebase SDK and that SDK uses Tasks. Tasks are great. They let you define blocks of code to run one after another (chaining) and decide what happens if a task fails, throws exceptions, etc. You can read more about them on Microsoft docs.

A big aspect of Tasks that differentiates them from Coroutines is that they can run on background threads. Unity logs uncaught exceptions on the main thread to the console by default. A very handy tool for finding bugs in your code and fixing them. For background threads however, the exceptions will remain silent in Unity. This can cause weird issues in your code.

Another thing about Tasks is that they encapsulate both the code to run and the result of the execution. So if a task fails (as in throws an exception), it will catch the exception and prevent it from going up the stack. The task will save the exception so that any subsequent tasks or routines can examine what happened later.

Let’s take a look at a real example:

TaskCompletionSource is a utility class for making tasks. We can control the task’s state using SetResult, SetCanceled and SetException. tcs.Task returns a Task.

Task.IsCanceled and Task.IsFaulted can be used to examine whether a Task ran successfully to completion or not. The if-else clause looks a bit cumbersome, specially since it needs to be written per task.

FromCurrentSynchronizationContext() makes the continuation to run on the same thread as the current Task. Since Start() is guaranteed to get called on the main thread in Unity, this will cause all code to run on the Main Thread.

You can schedule Task continuations to run on the main thread more reliably by using Firebase.Extensions.TaskExtension.ContinueWithOnMainThread (Firebase SDK). You can also use MainThreadUnityDispatcher library from Github.

The Logging Problem

Not let’s imagine the case where we set the task to run successfully but our setup is buggy and myObj field is null. Line 16 calls MyMethod on the Main Thread, yet line 6 never prints anything to console!

So out Unity console will look something like this:

How confusing! The simple 3 line code of MyMethod is not running to completion and there is no errors printed to console.

Note that if we call MyMethod from the main thread directly, not as part of a Task, we would get an error log with the exception info which would make it clear what was happening and WHY the code in MyMethod wouldn’t run.

What is Happening?

Line 5 throws an exception in both cases.

When MyMethod is called directly, the exception propagates up the stack till Unity catches it and prints it to the console.

When MyMethod is called from a Task on the main thread, the exception is caught by the Task and stored in the task object. Unity Editor will never see the exception.

How to Fix This?

The fix is simple in principle. We can do the same thing we did for our task to begin with. Check its result of our continuation task and print errors to console:

Now the console shows the exception just like it would if we weren’t using Tasks.

A Cleaner Solution

Having to make a task continuation just to print errors to console will clutter our codebase. Let’s make an extension method for Task instead!

Going back to our original code, we can rewrite it like this:

Much more concise!

--

--

No Such Dev

Software Engineer | Indie Game Developer | Founder of No Such Studio. Follow me to learn how to make video games with Unity. http://www.nosuchstudio.com