How to catch ALL exceptions in async HttpClient call? - c#

I have the following function that downloads a web page:
static bool myFunction(int nmsTimeout, out string strOutErrDesc)
{
//'nmsTimeout' = timeout in ms for connection
//'strOutErrDesc' = receives error description as string
bool bRes = false;
strOutErrDesc = "";
HttpClient httpClient = null;
System.Threading.Tasks.Task<string> tsk = null;
try
{
httpClient = new HttpClient();
tsk = httpClient.GetStringAsync("https://website-to-connet.com");
if (tsk.Wait(nmsTimeout))
{
if (tsk.Status == System.Threading.Tasks.TaskStatus.RanToCompletion)
{
string strRes = tsk.Result;
strRes = strRes.Trim();
if (!string.IsNullOrWhiteSpace(strRes))
{
bRes = true;
}
else
{
//Empty result
strOutErrDesc = "Empty result";
}
}
else
{
//Bad task completion
strOutErrDesc = "Bad completion result: " + tsk.Status.ToString();
}
}
else
{
//Timed out
strOutErrDesc = "Timeout expired: " + nmsTimeout + " ms.";
}
}
catch (Exception ex)
{
//Error
strOutErrDesc = "Exception: " + ex.Message;
if (tsk != null)
{
strOutErrDesc += " -- ";
int c = 1;
foreach(var exc in tsk.Exception.InnerExceptions)
{
strOutErrDesc += c.ToString() + ". " + exc.InnerException.Message;
}
}
bRes = false;
}
return bRes;
}
I thought that my try/catch construct was enough to catch all exceptions in it.
Until I found the following exception and the Windows error message that the app crashed:
Unhandled Exception: System.AggregateException: 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. ---> System.Net.Http.HttpRequestException:
Response status code does not indicate success: 503 (Service
Unavailable).
--- End of inner exception stack trace ---
at System.Threading.Tasks.TaskExceptionHolder.Finalize()
What is this and how do I catch it?

The app crashed, because try/catch is not "catch it everywhere" hack, it only catches exceptions that are thrown on the same call stack, or same call context.
You, on the other hand, for some reason use synchronous method to launch async tasks, those are run on other threads, and the context for them is lost.
Either use sync versions of those methods, or better else, use async method and await on your async tasks, that will preserve the call context and will allow you to catch any exception thrown from within with your try/catch block.

Note that what is described here is an old behaviour (as stated in the comments) and it does not happen with .NET 4.5 and newer.
What is happening is that the Task did not finish successfully and you are not checking for errors. When the garbage collector tries to clean up the Task object, it finds the unhandled exception there and throws it in an AggregateException. That is the exception is not actually thrown in your try block (it's even on different thread), hence your catch cannot catch it.
What you want to do is to properly await the created task. You might want to read up on async/await in C# at this point. If you want the task to be cancellable, you may have to use GetAsync with a cancellation token or you will have to wait for GetStringAsync to finish at some point.
If you for some reason do not want to use the asynchronous way of awaiting (you should!), you can still use tsk.Wait();. This will however wrap the thrown exception in an AggregateException and the call will be synchronous.
And if you really cannot stay around and wait for your function to finish, you can see in this question, how to handle the exception checking automatically with a continuation task.
However I would really advise you to use async/await and properlly check how the tasks finish and what did they throw.

Related

Logging exceptions in fire&forget tasks

I try to start some action in background, I am not interested in its result, but in case of an error I'd like to log it, and - of course - prevent the application (here: a Windows service) from crashing.
public static void CreateAndStartTaskWithErrorLogging(Action _action, string _componentName, string _originalStacktrace = null)
{
DateTime started = HighPrecisionClock.Now;
Task task = new Task(_action);
task.ContinueWith(_continuation => _continuation.LogExceptions(_componentName, started, _originalStacktrace));
task.ConfigureAwait(false);
task.Start();
}
internal static void LogExceptions(this Task _t, string _componentName, DateTime _started, string _originalStacktrace = null)
{
try
{
_t.Wait(1000);
}
catch (Exception ex)
{
Logger.LogError(_componentName, $"An exception occurred in a fire-and-forget task which was started at {_started}.\r\n" +
$"The original stack trace is:\r\n{_originalStacktrace}");
Logger.LogException(_componentName, ex);
}
try
{
_t.Dispose();
}
catch (Exception dex)
{
Logger.LogException(_componentName, dex);
}
}
Without ConfigureAwait(false) and without _t.Dispose(), the catch works and logs the exception. But the application crashes several seconds later (i.e. on the Finalizer thread?). The entry in the Microsoft Event Viewer shows that exception.
With ConfigureAwait and _t.Dispose(), I do not see the exception in the logs, the application just crashes.
What's wrong with the idea shown above?
Edit:
Meanwhile I tested without ConfigureAwait but with _t.Dispose. I could catch about 10 such exceptions, and none made the application crash. That seems to solve the issue, but I do not understand the reason for that, so the situation is still bad.
What does ConfigureAwait(false) do to Exceptions in the task (or in tasks started within that task, e.g. by a Parallel.ForEach further down)?
Why does the Dispose - which is called on the continuation, not the task proper according to a comment - prevent the crash (the Finalizer does not call Dispose, but Dispose may set some flags influencing its behavior)?
Edit 2:
Also that does not work all the time, only most of the time. Suggested solution 1 below also fails sometimes.
In the crashing context, the function is called with Utilities.TaskExtensions.CreateAndStartTaskWithErrorLogging(() => DataStore.StoreSyncedData(data), Name);, where DataStore is set to a composite which in turn calls Parallel.ForEach(m_InnerDataStores, _store => { _store.StoreSyncedData(_syncedData); }); on its members. One of them writes a video with the Accord library, which sometimes causes an AccessViolation at <Module>.avcodec_encode_video2(libffmpeg.AVCodecContext*, libffmpeg.AVPacket*, libffmpeg.AVFrame*, Int32*), i.e. the exception may come from non-managed code.
Of course, I could try to catch it somewhere down there - but that's not the objective of this method. I expect it to be able to safely run any code in the background without crashing the application.
This is my suggestion for logging errors:
public static void OnExceptionLogError(this Task task, string message)
{
task.ContinueWith(t =>
{
// Log t.Exception
}, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
}
Usage example:
var task = Task.Run(action);
task.OnExceptionLogError("Oops!");
try
{
await task;
}
catch
{
// No need to log exception here
}

Looking for what caused the "A Task's exception(s) were not observed..."

First of all, let me say that, it's so hard to explain my problem in detail, but I will try my best. I will update with a detailed explanation or more codes which is used by me that probably caused the exception. And I'm sorry if my code is messy.
There are many SO questions with the same title that I have read, but I have no luck at all. I have a very little understanding of Thread/Task/Dispatcher here, so please guide me if you find something wrong about my code.
Intoduction
My application does the background task by timer every n minutes.
The background task: fetches data from API, then generates Window element as a form to contain the data, then prints them.
The problem: The exception has already occured twice at the moment, which prevents two form documents to be generated so they are not printed.
The detailed exception which is get from TaskScheduler.UnobservedTaskException is:
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.
Stack Trace: N/A
Inner Exception:
System.Collections.ObjectModel.ReadOnlyCollection`1[System.Exception]
Here is my piece of code that may be useful for you to find the source of the problem:
public void BackgroundTask(object sender, EventArgs e)
{
Application.Current.Dispatcher.Invoke(
new Action(GetInvoiceData),
DispatcherPriority.Background,
null
);
}
...where GetInvoiceData is:
public async void GetInvoiceData()
{
try
{
JsonData = await ApiHelperInstance.Post(ApiParam);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (!string.IsNullOrEmpty(JsonData))
{
var apiReturn = new ApiReturn();
try
{
apiReturn = JsonConvert.DeserializeObject<ApiReturn>(JsonData);
}
catch (JsonException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (apiReturn.Result != null)
{
foreach (ApiResult apiResult in apiReturn.Result)
{
InvoiceQueue.Enqueue(new Invoice(apiResult));
}
var worker = new BackgroundWorker();
worker.DoWork += GenerateDocumentAndPrint;
worker.RunWorkerAsync();
}
}
}
}
}
...and GenerateDocumentAndPrint is:
public void GenerateDocumentAndPrint(object sender, DoWorkEventArgs e)
{
while (InvoiceQueue.Count != 0)
{
Dispatcher.Invoke(() =>
{
Invoice invoice = InvoiceQueue.Dequeue();
var invoiceForm = new InvoiceForm();
var shippingLabelForm = new ShippingLabelForm();
invoiceForm.Dispatcher.Invoke(async () =>
{
var invoiceTmp = invoice;
var invoiceDoc = new FixedDocument();
try
{
invoiceDoc = await invoiceForm.CreateDocument(invoiceTmp);
}
finally
{
InvoiceDocumentName = PrintJobNameSub + " - Invoice #" + invoice.TransOrder.TransNumber;
PrintHelperInstance.SetPrinterByName(InvoicePrinterName);
PrintHelperInstance.PrintDocument(invoiceDoc.DocumentPaginator, InvoiceDocumentName);
invoiceForm.Close();
}
}, DispatcherPriority.ContextIdle);
shippingLabelForm.Dispatcher.Invoke(async () =>
{
var invoiceTmp = invoice;
var shippingLabelDoc = new FixedDocument();
try
{
shippingLabelDoc = await shippingLabelForm.CreateDocument(invoiceTmp);
}
finally
{
ShippingLabelDocumentName = PrintJobNameSub + " - Shipping Label #" + invoice.TransOrder.TransNumber;
PrintHelperInstance.SetPrinterByName(ShippingLabelPrinterName);
PrintHelperInstance.PrintDocument(shippingLabelDoc.DocumentPaginator, ShippingLabelDocumentName);
shippingLabelForm.Close();
}
}, DispatcherPriority.ContextIdle);
}, DispatcherPriority.Normal);
}
}
...and async method CreateDocument from both of InvoiceForm and ShippingLabelForm contains await Task.Delay(delay).
Is there any mistake I made from my code? Is it caused by wrong use of Dispatcher.Invoke? Is it caused by wrong use of DispatcherPriority enum? Is it something wrong with the Task.Delay operation?
TaskScheduler.UnobservedTaskException gets exceptions when a task's exceptions are not observed. If you await all your tasks, then this event will never fire.
Note that this event does not necessarily mean an error in the strict sense of the term. For example, this can happen if you abandon a task - fairly common if your code contains any Task.WhenAny calls. This can also happen if a "fire and forget" task throws an exception. In both of these cases, it's not actually an error. In the WhenAny case, a different task already completed the Task.WhenAny, so you don't care if another task threw an exception. In the case of "fire and forget", "forget" literally means "I don't care about exceptions", so you shouldn't care if it threw an exception.
This event only indicates an error if you are accidentally missing an await. The easiest way to find a missing await is by examining the call stack of the inner exceptions and then examining the callers of that method, etc., until you find the one that is not properly awaiting the task.

Catching exceptions in async code called synchronously

I have thrift service for authentication. catch (AccountNotFoundException) doesn't catch the exception unless I call it in Task.Run. The strange thing is that test case is fine. Why? Is it because task.start() is on the different level than catch?
public override string GetUserNameByEmail(string email)
{
var task = client.GetUserByEmail(email, false);
return task.Result;
// I changed to
// return Task.Run(() => client.GetUserByEmail(email, false)).Result.UserName;
// and I was able to catch the exception
}
public async Task<AccountDetails> GetAccountDetailsByEmail(string email)
{
try
{
return await Call(() => client.getAccountDetailsByEmail(email));
}
catch (AccountNotFoundException)
{
return null;
}
}
private async Task<T> Call<T>(Func<T> call)
{
try
{
transport.Open();
var thriftTask = new Task<T>(call);
thriftTask.Start();
return await thriftTask;
}
catch (DatabaseException e)
{
Logger.Error(e);
throw;
}
finally
{
transport.Close();
}
}
Test case works just fine
[TestMethod]
public async Task Nonexisting_User_I_Expect_To_Be_Null()
{
var user = Provider.GetUser("idontexist#bar.com", false);
Assert.IsNull(user);
}
EDIT:
I have a following theory why my code run ok: The code was working because I was lucky. Request and async was handled by the same thread so it shared the same context so it didn't block.
First, you shouldn't be calling asynchronous methods synchronously. As I describe on my blog, the approach you're using is prone to deadlocks.
The reason you're seeing an unexpected exception type is because Result will wrap any task exceptions in an AggregateException. To avoid this, you can call GetAwaiter().GetResult().
This doesn't have anything to do with Start, but since you mention it, the Start member doesn't really have a use case. There's never a good reason to use it. Instead, use Task.Run:
var thriftTask = Task.Run(call);
See here for details of exception handling for async code. It may be that you're catching an AccountNotFoundException, when you really want to be catching an Exception, which will have an InnerException set to the AccountNotFoundException:
https://msdn.microsoft.com/en-us/library/0yd65esw.aspx
An excerpt:
The task's IsFaulted property is set to True, the task's Exception.InnerException property is set to the exception, and the exception is caught in the catch block.
public async Task DoSomethingAsync()
{
Task<string> theTask = DelayAsync();
try
{
string result = await theTask;
Debug.WriteLine("Result: " + result);
}
catch (Exception ex)
{
Debug.WriteLine("Exception Message: " + ex.Message);
}
Debug.WriteLine("Task IsCanceled: " + theTask.IsCanceled);
Debug.WriteLine("Task IsFaulted: " + theTask.IsFaulted);
if (theTask.Exception != null)
{
Debug.WriteLine("Task Exception Message: "
+ theTask.Exception.Message);
Debug.WriteLine("Task Inner Exception Message: "
+ theTask.Exception.InnerException.Message);
}
}
private async Task<string> DelayAsync()
{
await Task.Delay(100);
// Uncomment each of the following lines to
// demonstrate exception handling.
//throw new OperationCanceledException("canceled");
//throw new Exception("Something happened.");
return "Done";
}

Problems with handling exceptions from async method with tasks

I hope you can help me with that problem.
I have a method that does specific actions, for example, I am sending a HttpWebRequest. There I can get a WebException, so I put it in a Try-Catch-block to rethrow exceptions for more specific exception messages.
Like that: (This is in a method called doWebRequest)
try
{
// HttpWebRequest here
}
catch (WebException ex)
{
throw new WebException(String.Format("My special additional message {0}", ex.Message);
}
Well, so this works, when I call this function normally.
But now I want an async way to do this. What I made to call this method in an async method:
public void DoRequestAsync()
{
Task internalRequest = new Task(doWebRequest);
internalRequest.ContinueWith(InternalUpdateSearchExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
internalRequest.Start();
}
So this makes a new task and then calls the method doWebRequest async.
To handle errors now, because I know it is different on async, I made this handler, which you can also see in internalRequest.ContinueWith. Looks like that:
private void InternalUpdateSearchExceptionHandler(Task task)
{
var ex = task.Exception;
if (ex.InnerException is WebException)
{
if ((ex.InnerException as WebException).Status == WebExceptionStatus.ProtocolError)
{
throw new WebException(ex.InnerException.Message);
}
else
{
throw new Exception("There was no response from the server.");
}
}
}
But this is not executing any exceptions. I don't know why.
At first I thought, this is because it cannot take the InnerException as a WebException, or would that work? If not, please tell me what to do here. But even when I throw an exception without any queries here, it did not throw any exceptions while debugging. Why is that?
Help is appreciated. When something is not clear, ask me. ;)
Your continuation is throwing an exception, but when that continuation throws an exception all that happens is the Task that represents that continuation is marked as Faulted with the given exception as its Exception. You currently are ignoring the task created by calling ContinueWith, so there is nothing to observe this exception.
DoRequestAsync likely shouldn't be void; rather it should return a Task, specifically the one created by calling ContinueWith, so that whoever calls this method can observe any exceptions thrown.
Also note that you could do this much more simply using async:
public async Task DoRequestAsync()
{
try
{
var content = await new WebClient()
.DownloadStringTaskAsync("address");
}
catch (WebException ex)
{
throw new WebException(String.Format("My special additional message {0}", ex.Message);
}
}

Task Parallel Library and exception

I have an application that are executing 4 different jobs parallel. I use the parallel task library.
The code looks like this:
while (true)
{
var fetch = new FetcherHandler();
var tasks = new Task[4];
tasks[0] = Task.Factory.StartNew(fetch.GetJobs);
tasks[1] = Task.Factory.StartNew(fetch.GetStatusNew);
tasks[2] = Task.Factory.StartNew(fetch.GetJobsForStatus);
tasks[3] = Task.Factory.StartNew(fetch.GetStatusOld);
Task.WaitAll(tasks);
Thread.Sleep(10000);
}
The thread.Sleep above will never be reached since all these 4 tasks is in a never ending loop. Code example for the first task:
public void GetJobs()
{
while (true)
{
try
{
handler.GetNewJob();
}
catch (Exception exception)
{
var mail = new MailService();
mail.SendExeption("Error has accured in GetJobs", exception);
throw;
}
}
}
Thread.Sleep(15000); }}
The above code works great, it does some jobs and then it sleeps for 15 sec, and then it does it again. The problem occurres when I get an exception.
The exception is caught alright, and sends me an mail, but the thred doesn't continue in the loop after the exception, so the GetNewJob() method will never get executed again.
Any ideas what happens to the thread when exception hits, and how I can "save" the thread so it can continue?
Becasue you throw an exception, so the exception propagates on top of the stack and break execution of the source thread.
catch (Exception exception)
{
var mail = new MailService();
mail.SendExeption("Error has accured in GetJobs", exception);
throw; //THIS LINE !
}
You can use an event to raise it, say, ExceptionCaught(..) and listen for that event inside your program.
Just remove the throw; at the end of your catch clause.
From MSDN:
A throw statement can be used in a catch block to re-throw the
exception that is caught by the catch statement.
So you're rethrowing the caught exception which causes the thread to interrupt, never executing again...
More information: What happens when a .NET thread throws an exception?

Categories