All,
I have a production ASP.NET core 2.2 application managing websockets connections. Very occasionally we get exception unhandled errors inside a try catch block if a client resets the connection during transmission, which should be a condition we can handle in code (we disconnect the client).
The try catch block in the code below isn't catching the occasional client disconnect / resets as the error is in CoreLib.dll not the C# code, I suspect it might have to do with not setting up async function correctly??
Any ideas what is causing it, and how to work around it?
// Process web sockets request
private static async Task<bool> ReceiveBin(MQTT.Broker myClient, CancellationToken ct = default(CancellationToken))
{
var buffer = new ArraySegment<byte>(new byte[RECV_BUFF_SIZE]);
using (var ms = new MemoryStream())
{
WebSocketReceiveResult result = null;
try
{
do
{
ct.ThrowIfCancellationRequested();
result = await myClient.socket.ReceiveAsync(buffer, ct); <== Unhandled exception breaks here
ms.Write(buffer.Array, buffer.Offset, result.Count);
}
while (!result.EndOfMessage);
}
catch (Exception ex) // v1 ASP Core sometimes throw errors when client aborts session
{
CloseWSSess(myClient, "Error receiving from client - resetting session. Error: " + ex.Message);
return false;
}
...
Error from the await statement => An exception of type 'System.OperationCanceledException' occurred in System.Private.CoreLib.dll and wasn't handled before a managed/native boundary
Related
I have this code below that sends message to azure EventHub, at first it works fine
public async Task SendMessage(string message) {
var producer = new EventHubProducerClient(this.connectionString, this.eventHubName);
using (EventDataBatch eventBatch = await producer.CreateBatchAsync()) {
if (!eventBatch.TryAdd(new EventData(message))) {
throw new Exception($"Event is too large for the batch and cannot be sent.");
}
try {
await producer.SendAsync(eventBatch);
}
catch(Exception ex) {
saveLog(message, ex);
}
finally {
await producer.DisposeAsync();
}
}
}
but in Application Insights I'm facing this message exception:
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. A connection attempt
failed because the connected party did not properly respond after a
period of time, or established connection failed because connected
host has failed to respond
Exception type:
System.Net.Sockets.SocketException
I've tried to add a ContinueWith (OnlyOnFaulted or OnlyOnCanceled), as below, to log it,
but now I get in the catch Exception (not in ExceptionHandle ) as "A task was canceled"
try {
await producer.SendAsync(eventBatch)
.ContinueWith(t => ExceptionHandle(t, message), TaskContinuationOptions.OnlyOnFaulted);
}
How can I handle these exceptions that App Insights display?
If you need to handle the task exception by using a ContinueWith (OnlyOnFaulted or OnlyOnCanceled) we can get this exception in catch block only. By default, if the task runs successfully, it throws Exception if the Task IsFaulted we can get the exception from Catch Block.
To fix this issue:
Use UnobservedTaskException event which observe the Task Exceptions and it doesn't terminate by default. The exception can be handled by the runtime.
I'm trying to convert my sync functions to async. In all my sync functions I have a cancellation token which is used on function, task and parallel blocks. I have a try/catch block before calling the async function, but I'm getting an unhandled exception:
Exception thrown: 'System.OperationCanceledException' in
System.Threading.Tasks.Parallel.dll An exception of type
'System.OperationCanceledException' occurred in
System.Threading.Tasks.Parallel.dll but was not handled in user code
The operation was canceled.
My async function:
public async Task DecodeAsync(string? fileFullPath, FileDecodeType fileDecodeType, OperationProgress? progress = null) =>
await Task.Run(() => Decode(fileFullPath, fileDecodeType, progress), progress?.Token ?? default);
How I call it:
try
{
await SlicerFile.DecodeAsync(fileName, fileDecodeType, Progress);
}
catch (OperationCanceledException) { } // Do not work!
catch (Exception exception) // Works for other exceptions
{
await this.MessageBoxError(exception.ToString(), "Error opening the file");
}
catch (OperationCanceledException) is never reached nor catch (Exception exception) in a cancel event. As my try is at top most, why doesn't it catch the exception?
But if I do:
public async Task DecodeAsync(string? fileFullPath, FileDecodeType fileDecodeType, OperationProgress? progress = null) =>
await Task.Run(() => throw new Exception("Test"));
I get the exception catch on the generic Exception (it's handled)
In other hand with old code it's working and handling the OperationCanceledException:
var task = await Task.Factory.StartNew( () =>
{
try
{
SlicerFile.Decode(fileName, fileDecodeType, Progress);
return true;
}
catch (OperationCanceledException) {} // Works!
catch (Exception exception)
{
Dispatcher.UIThread.InvokeAsync(async () =>
await this.MessageBoxError(exception.ToString(), "Error opening the file"));
}
return false;
});
What am I doing wrong?
The results of Task.Run don't need to be awaited right there necessarily. You can just return the running Task and then the method doesn't need to be awaited or be async any longer.
Task DecodeAsync(string? fileFullPath, FileDecodeType fileDecodeType, OperationProgress? progress = null) => Task.Run(() =>
Decode(fileFullPath, fileDecodeType, progress), progress?.Token ?? default);
And since you're passing in progress with a token, you can monitor that to exit the decode method cleanly instead of trying to catch and ignore the operation cancelled exception.
You'll have better luck if you make the decode method itself asynchronous if you can. It already returns a Task, so it could return a Task (or whatever). Your old code is also asynchronous in the same way, so there's no advantage to your new code that I can see.
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.
There are some tasks that I attached an action to. I have a middleware for observing all of the exceptions. Now, there is a problem with thread exception. I've already thrown an exception manually in body of the action, but when I throw an exception the application goes to break mode state and I can't monitor the exceptions.
Before mvc in configure, I put my error handling middleware
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context /* other dependencies */)
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
var code = HttpStatusCode.InternalServerError;
var result = JsonConvert.SerializeObject(new { error = exception.Message });
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
}
//and this is the piece of code that run all tasks.
foreach (var item in BotList)
{
BotHandler BotObject = new BotHandler(item,conn);
Task.Run(() => { BotObject.Run();});
}
//
public void Run()
{
//BotClient.StopReceiving();
BotClient.OnMessage += BotOnMessageReceived;
BotClient.OnMessageEdited += BotOnMessageReceived;
BotClient.OnCallbackQuery += BotOnCallbackQueryReceived;
}
private async void BotOnMessageReceived(object sender, MessageEventArgs messageEventArgs)
{
try
{
//do something
string a = null;
var b = a.ToString();
}
catch(Exception exp )
{
throw exp
}
}
}
As I understood you run this code in action of controller:
//and this is the piece of code that run all tasks.
foreach (var item in BotList)
{
BotHandler BotObject = new BotHandler(item,conn);
Task.Run(() => { BotObject.Run();});
}
The main problem is that you are trying to run the task for an already finished request. That is why ExceptionHandlingMiddleware (and actually other middlewares) can't handle anything. To fix your issue you can add try/catch block to handle an unexpected exception.
I would strongly advise not to start a background task during an HTTP request. It's error-prone approach as a task may shut down at any time and you won't even notice. Instead of this approach, it's better to use background task (msdn, SO Discussion), some kind of AWS lambda/Azure function or another task scheduler.
In case you need to do some recalculation job after HTTP method is called you may consider async message processing to trigger the process.
if you go here https://msdn.microsoft.com/en-us/magazine/jj991977.aspx you will read that.
Exceptions from an Async Void Method Can’t Be Caught with Catch
So you cannot catch exception (the middleware cannot) which you are throwing in your BotOnMessageReceived method (in a Catch part).
So you have 2 solutions.
Remove async keyword
Or Catch app unhandled exceptions if its possible. For example in normal .net framework console app, you have event in App called unhandled exceptions, and can handle such situations like yours.
I have also found smth here, maybe it will help
How do I catch unhandled exceptions in ASP .NET Core 2.0 before the page is rendered?
My TCP server will crash if my client ungracefully disconnects. The exception is occurring when reading from a created client in the TCPServer.
unable to read data from the transport connection
public async void StartReadAsync() {
while (await ReadAsync());
}
private async Task<bool> ReadAsync() {
int amountRead = await stream.ReadAsync(readBuffer, 0, readBuffer.Length, cts.Token).ConfigureAwait(false);
}
I crash on the await stream.ReadAsync as soon as the client DCs.
Put the instruction into a TRY-CATCH structure
try
{
int amountRead = await stream.ReadAsync(readBuffer, 0, readBuffer.Length, cts.Token).ConfigureAwait(false);
}
catch
{
}
I´m not supposing you want to know the exception itself but just to avoid a crash in program. If you want, you may set CATCH to display which exception is being performed.
See more at:
https://msdn.microsoft.com/pt-br/library/0yd65esw.aspx