How to catch exception thrown in a task in a task - c#

i need help with catching exceptions in C#. I have a Windows service which is acting like a wrapper for other modules, so to not have multiple Windows Services i start all modules/agents thats how we call them in that Wrapper Windows Service. Each of this 'agent' is started in a own Task. I am not in control what the agent itself is doing so it can be and will from time to time is such a agent starting also a task or thread and if there an exception is getting thrown, i am not able to catch it. I tried different things but was not able to do so. So if such a exception occurs in production my whole service is crashing and all agents with it, which is a nightmare. I try to simplify it with an example code:
public class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
AppDomain.CurrentDomain.UnhandledException += (o, e) =>
{
Console.WriteLine("CurrentDomain Unhandled Exception: {0}", e.ExceptionObject);
};
TaskScheduler.UnobservedTaskException += (s, e) =>
{
Console.WriteLine("TaskScheduler.UnobservedTaskException Unhandled Exception: {0}", e.Exception);
e.SetObserved();
};
Task.Factory.StartNew(() =>
{
Task.Factory.StartNew(() => throw new Exception("I am a exception ! Catch me !"));
}, TaskCreationOptions.LongRunning)
.ContinueWith((t) =>
t.Exception.InnerExceptions.ToList().ForEach(e => Console.WriteLine("Error executing task.{0}", e)),
TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("If you read this, application is not crashed!");
Console.ReadKey();
Task.Factory.StartNew(() =>
{
throw new Exception("I am a exception ! Catch me !");
});
}
}
So how to catch the exception ? It will not get fetched by any of my handlers.
Important is that i have no influence of that part of code, thats my 'agent' :
Task.Factory.StartNew(() => throw new Exception("I am a exception ! Catch me !"));
Everything else i am able to change.
Edit:
unfortunately provided solution seems to not work for me. I am still not able to catch the exception which occurs. Maybe its getting more clear when i show my original code:
private async Task StartAgent(IAgent agent)
{
_logger.LogInfo("Agent starting with instanceId {0}", agent.GetInstanceGuid());
if (agent == null)
{
throw new ArgumentNullException("agent");
}
try
{
Task task = await Task.Factory.StartNew(async () =>
{
agent.Start();
}, TaskCreationOptions.LongRunning);
await task.ContinueWith((t) =>
{
var aggException = t.Exception.Flatten();
foreach (var exception in aggException.InnerExceptions)
_logger.LogError("Error executing agent task.{0}", exception, t.Id);
}, TaskContinuationOptions.OnlyOnFaulted);
_agents[agent.GetInstanceGuid()].Task = task;
_agents[agent.GetInstanceGuid()].LastTaskStatus = task.Status;
}
catch (Exception e)
{
_logger.LogError("Exception in Agent Task.",e);
}
}
So the agent.Start() is what i am calling in the task everything what happens inside i don't know. The agent can create tasks, threads everything he wants. Start() is also void and i can't change the interface to await it.

[Task.Factory.StartNew] is wrapped in a void method.
Well, then, the code is deliberately ignoring all exceptions. This kind of "fire and forget" is problematic precisely because it ignores the returned task. All exceptions are placed on that task, which is then ignored.
TaskScheduler.UnobservedTaskException catches it
UnobservedTaskException or AppDomain.FirstChanceException are your only real options. Neither of these are particularly nice (i.e., they're global handlers), but they're your only option because the code outside your control is explicitly ignoring exceptions.

Thanks for all the answers. I found a easy solution now which works for me. All i had to do is adding in my 'runtime' part of my app.config this flag
<legacyUnhandledExceptionPolicy enabled="1" />
Like described here: How to prevent an exception in a background thread from terminating an application?
I had the problem even if i was able to catch my exception my service still stops afterwards.

Related

How to catch exceptions from Task.Run() if not awaited? [duplicate]

This question already has an answer here:
Exception handling in fire and forget for C# 5 (in .net 4.5)
(1 answer)
Closed 2 years ago.
Let's assume I have a console application with Main method, something like this:
public static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
{
Console.WriteLine("App Unobserved");
};
TaskScheduler.UnobservedTaskException += (sender, eventArgs) =>
{
Console.WriteLine("Task Unobserved");
};
Task.Run(async () => await MyAwesomeMethod());
// other awesome code...
Console.ReadLine();
}
public static async Task MyAwesomeMethod()
{
// some useful work
if (something_went_wrong)
throw new Exception();
// other some useful work
}
So, I just run MyAwesomeMethod (fire-and-forget), and want to do some other job, but I also want to know if there any unhandled exceptions. But application finishes successfully without any sign of problem (exception is just swallowed).
How can I handle exception from MyAwesomeMethod(), without awaiting it or using Task.Run(...).Wait()?
So, I just run MyAwesomeMethod (fire-and-forget)... but I also want to know if there any unhandled exceptions. But application finishes successfully without any sign of problem (exception is just swallowed).
That's not "fire and forget", then. "Fire and forget" literally means that you don't care when (or whether) the task completes (or errors).
How can I handle exception from MyAwesomeMethod(), without awaiting it or using Task.Run(...).Wait()?
Use await anyway:
Task.Run(async () => {
try {
await MyAwesomeMethod();
} catch (Exception ex) {
Console.WriteLine(ex);
}
});
You can check the status of your task once it's done.
Task.Run(() => MyAwesomeMethod()).ContinueWith((task) =>
{
if (task.Status == TaskStatus.RanToCompletion && task.Result != null)
{
}
else
{
try
{
Logger.LogError(task.Exception.ToString());
Logger.LogMessage("something_went_wrong");
}
catch { }
}
});
You could for example wrap the code in the background task in a try...catch block and raise an event as soon as you enter the catch block (if you do).
Like
event EventHandler<Exception> exceptionInWorker;
and in the task do
try
{
//do something
}
catch (Exception e)
{
exceptionInWorker?.Invoke(this, e);
}
You can subscribe to TaskScheduler.UnobservedTaskException event as you do but with a handler that takes UnobservedTaskExceptionEventArgs as its second parameter, through it you could access the unhandled exception via its Exception property and log all info about it.

async/wait: bubbleup exceptions?

This pertains to a program that has to collect some info from a server on the web.
Question: How can I get the exceptions from GetServerResponseAsync and Check to bubble up through CheckAsync to the Main program?
As depicted here, they do not. ErrorHandler never gets hit.
My Main program:
....
try
{
Task.Run(() => CheckAsync());
// bubble to here?
ReadConfiguration();
}
catch (Exception ex)
{
// gather and handle all exceptions here
ErrorHandler.NotifyMe(new[] { "some message" }, ErrorType.Stop); // never gets hit
}
public async Task CheckAsync()
{
await GetServerResponseAsync("slm_check"); // may throw exception
...
if (.....)
throw new Exception("...");
...
}
public async Task GetServerResponseAsync(string command)
{
...
// client = HttpClient()
using (apacheResponse = await client.GetAsync(ServerUrl + "...."))
{
if (....)
throw new Exception("Internal web server error", new Exception("Maybe MySQL server is down"));
using (HttpContent content = apacheResponse.Content)
{
if ( ....)
throw new Exception("error message");
}
}
}
How can I get the exceptions from GetServerResponseAsync and Check to bubble up through CheckAsync to the Main program?
Use await to consume your tasks, instead of ignoring them.
Specifically, this line:
Task.Run(() => CheckAsync());
is getting a Task back from the Task.Run method, which is then ignored. Instead of ignoring that task, the code should be awaiting it:
await Task.Run(() => CheckAsync());
As other commenters have pointed out, the Task.Run here doesn't really make sense. If your operation is asynchronous, it shouldn't need to also run on a background thread. Usually. :) So if you take out the Task.Run, your code would look like:
await CheckAsync();
which will properly propagate the exception.

Debugger not breaking/stopping for exceptions in async method

When a debugger is attached to a .NET process, it (usually) stops when an unhandled exception is thrown.
However, this doesn't seem to work when you're in an async method.
The scenarios I've tried before are listed in the following code:
class Program
{
static void Main()
{
// Debugger stopps correctly
Task.Run(() => SyncOp());
// Debugger doesn't stop
Task.Run(async () => SyncOp());
// Debugger doesn't stop
Task.Run((Func<Task>)AsyncTaskOp);
// Debugger stops on "Wait()" with "AggregateException"
Task.Run(() => AsyncTaskOp().Wait());
// Throws "Exceptions was unhandled by user code" on "await"
Task.Run(() => AsyncVoidOp());
Thread.Sleep(2000);
}
static void SyncOp()
{
throw new Exception("Exception in sync method");
}
async static void AsyncVoidOp()
{
await AsyncTaskOp();
}
async static Task AsyncTaskOp()
{
await Task.Delay(300);
throw new Exception("Exception in async method");
}
}
Am I missing something? How can I make the debugger to break/stop on the exception in AsyncTaskOp()?
Under the Debug menu, select Exceptions.... In the Exceptions dialog, next to the Common Language Runtime Exceptions line check the Thrown box.
I would like to hear if anyone found out how to get around this issue? Perhaps a setting in latest visual studio...?
A nasty but workable solution (in my case) was to throw my own custom Exception and then modify Stephen Cleary's answer:
Under the Debug menu, select Exceptions (You can use this Keyboard shortcut Control + Alt + E)... In the Exceptions dialog, next to the Common Language Runtime Exceptions line check the Thrown
box.
to be more specific i.e., add your custom Exception into the list, and then tick its "Thrown" box.
E.g:
async static Task AsyncTaskOp()
{
await Task.Delay(300);
throw new MyCustomException("Exception in async method");
}
I have wrapped the anonymous delegate in a try/catch inside the Task.Run(() =>.
Task.Run(() =>
{
try
{
SyncOp());
}
catch (Exception ex)
{
throw; // <--- Put your debugger break point here.
// You can also add the exception to a common collection of exceptions found inside the threads so you can weed through them for logging
}
});

How to let exception propagate up the stack from async methods? (cannot use async or await keywords)

Please, observe this simple code:
try
{
var t = Task.Factory.StartNew<bool>(() => { throw new Exception("aaa"); });
t.ContinueWith(_ => {}, TaskContinuationOptions.OnlyOnRanToCompletion).Wait();
}
catch (Exception exc)
{
Debug.WriteLine(exc);
}
I assumed that if t has an exception associated with it, then this exception will be rethrown as is by Wait(). However, the presence of the success only continuation seems to change this behavior. What is thrown instead is a "A task was canceled" exception.
Indeed, chaining a TaskContinuationOptions.NotOnRanToCompletion completion handler just before Wait() reveals that the task passed to it is not faulted, but cancelled:
t.ContinueWith(_ => { }, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(t2 => Debug.Assert(t2.IsCanceled), TaskContinuationOptions.NotOnRanToCompletion)
.Wait();
This is all a bit strange. It means, I cannot just chain my happy path completion handlers letting any exceptions just propagate to the ultimate rendezvous with the waiting thread.
What am I missing here?
NOTE
I am limited to .NET 4.0, so no await and async keywords.
What am I missing here?
You are missing .NET 4.5. Tasks are still useful without the await and async keywords, but if you want the "happy path" behavior you're talking about, you'll need to upgrade (see update below).
Because tasks are more complicated than standard run-through code, and because they can be joined together in various ways, you'll need to ask for the exception directly from the Task, rather than the exception thrown by the call to .Wait().
var t = Task.Factory.StartNew<bool>(() => { throw new Exception("aaa"); });
try
{
t.ContinueWith(_ => {}, TaskContinuationOptions.OnlyOnRanToCompletion)
.Wait();
}
catch (AggregateException exc)
{
Debug.WriteLine(exc.InnerExceptions[0]);// "A task was canceled"
Debug.WriteLine(t.Exception.InnerExceptions[0]);// "aaa"
}
Update: If you are using Visual Studio 2012, it appears that you can use the async and await keywords without upgrading to 4.5. Thanks to #zespri for pointing this out.
Update 2: If you want to catch and log the appropriate exception at the top level, just make a habit of wrapping your .Wait() methods in try/catch blocks, wrap the given exception.
try
{
t.Wait();
}
catch (AggregateException exc)
{
throw new Exception("Task Foo failed to complete", t.Exception);
}

is it possible to catch when any Task terminates due exception and log?

Is it possible to catch when any Task terminates due exception and log? I've added CurrentDomain_UnhandledException handling but this doesn't help.
I create tasks using Task.Factory.StartNew() as usual. When somewhere inside such task exception occurs it crashes silently (but it supposed to work forever, i'm also using LongRunning option). So I want to be notified about such behavior.
Ideallly I want to set some option somewhere to be notified when any Task crashes due exception.
If it is not possible then likely I should add something to each Task I create? Of course I can just add big try{} finally{} block inside each Task, but probably there are better solutions?
Assuming you have a Test as Task to run:
static int Test()
{
throw new Exception();
}
First Approach - Process exception in the caller's thread:
Task<int> task = new Task<int>(Test);
task.Start();
try
{
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex);
}
Note: The exception will be of type AggregateException. All actual exceptions are available through ex.InnerExceptions property.
Second Approach - Process exception in some task's thread:
Define the ExceptionHandler this way:
static void ExceptionHandler(Task<int> task)
{
var ex = task.Exception;
Console.WriteLine(ex);
}
Usage:
Task<int> task = new Task<int>(Test);
task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
task.Start();
Reference: How to: Handle Exceptions Thrown by Tasks
For tasks that you create yourself, it's reasonably simple: create your own methods which call Task.Factory.StartNew(), but then also call Task.ContinueWith(loggingDelegate, TaskContinuationOptions.OnlyOnFaulted before returning the task.
The problem is that that won't add a fault handler for tasks created by other bits of infrastructure - including by async methods in C# 5. It still might be useful to you though.
You can also use TaskScheduler.UnobservedTaskException, but as per the name that will only be called for exceptions which aren't already observed by something else. (Again, that may be fine for you...)
You can use an extension method that performs an operation when an exception has ocurred.
This happens when the Task gets Faulted. So if it has another tasks to continue with, the next one can check if the previous task was faulted and Log the exception.
I usually use this methods:
//If you want to chain more tasks..
public static Task<T> Continue<T>(this Task<T> task, Action<T> action)
{
if (!task.IsFaulted)
{
task.ContinueWith((t) => action(t.Result), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion);
}
return task;
}
public static Task OnException(this Task task, Action<Exception> onFaulted)
{
task.ContinueWith(c =>
{
var excetion = c.Exception;
onFaulted(excetion);
},
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
return task;
}
So you can use:
Task.Factory.StartNew(...).OnException(ex => Log(ex));
Hope it helps.
Wrap your task.Wait() in a try/catch block and catch AggregateException. Something like this -
Task<string[]> task1 = Task<string[]>.Factory.StartNew(() => GetAllFiles(path));
// Use this line to throw an exception that is not handled.
try
{
task1.Wait();
}
catch (AggregateException ae)
{
ae.Handle((x) =>
{
if (x is UnauthorizedAccessException) // This we know how to handle.
{
Console.WriteLine("You do not have permission to access all folders
in this path.");
Console.WriteLine("See your network administrator or try
another path.");
return true;
}
return false; // Let anything else stop the application.
});
}
Details can be found here - Handle exceptions thrown by Task.
You can create a OnlyOnFaulted continuation on your Task which observes the exception and logs/reports the problem.
t.ContinueWith(task =>
{
// Report and log error
}, System.Threading.CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
The above code will run the task on the UI thread because of TaskScheduler.FromCurrentSynchronizationContext(). This may be necessary if you are using winforms and need to notify the user.

Categories

Resources