1

I have an Azure Function HTTP triggered function which writes to Azure Table that may end in duplicated entries. I noticed that even if I try/catch'd the whole function, there will still be an Exception "leaked" to the function runner thus returning HTTP 500. Is there any way to catch this kind of exception?

Here's a minified version of the code:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;

namespace FunctionTest
{
    public class Entry
    {
        public string PartitionKey { get; set; }
        public string RowKey { get; set; }
    }
    public static class Debug
    {
        [FunctionName("Debug")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]
            HttpRequest req,
            [Table("Debug")]
            IAsyncCollector<Entry> tableBinding,
            ILogger log)
        {
            try
            {
                await tableBinding.AddAsync(new Entry()
                {
                    PartitionKey = "1111",
                    RowKey = "1111",
                });
                await tableBinding.FlushAsync();
            }
            catch (StorageException)
            {
                // we expect an Exception "The specified entity already exists"
                return new OkObjectResult("This passes test");
            }

            return new OkObjectResult("This passes test too");
        }
    }
}

The code is written under Azure Function runtime 2.0 (the .NET Core one).

Trigger /api/debug twice or more and you will see:

  • HTTP 500
  • The catch{} code is entered, and still returns an HTTP 500(!)
  • In Application Insights, two table dependency call per request (shouldn't happen, the documentation says table do not have auto retry)
Jamesits
  • 612
  • 7
  • 18
  • You aren't catching all the exception types so of course an exception can leak. Are you 100% sure that you aren't missing an exception by not having a generic `system.exception` catch block? – Charleh Feb 23 '19 at 09:52
  • 1
    It is also possible, but he said, that the flow enters the `catch` block and still reports 500. – kamil-mrzyglod Feb 23 '19 at 13:38
  • @Charleh I've already done experiments before I came here to ask, and catching Exception simply doesn't work. The code provided is a minimal version to reproduce the problem, it's not what I wrote in the actual project. – Jamesits Feb 23 '19 at 13:44

2 Answers2

2

I guess, that using IAsyncCollector<> breaks things here. If you want to avoid such problems, try to exchange the following binding:

[Table("Debug")] IAsyncCollector<Entry> tableBinding

to:

[Table("Debug")] CloudTable tableBinding

Then, instead of using tableBinding.AddAsync() use the following snippet:

var op = TableOperation.Insert(new Entry());
await tableBinding.ExecuteAsync(op);

With that approach, you should be able to catch the exception, without leaking it to the Functions runtime.

kamil-mrzyglod
  • 4,948
  • 1
  • 20
  • 29
-1

Your try/catch block should look like following to catch all errors

try
{

}
catch (StorageException)
{
    return new OkObjectResult("This passes test");
}
catch (Exception ex)
{
    // return different error code
}
Rahul Ruikar
  • 1,076
  • 6
  • 9
  • The code provided is only a minimal version to reproduce the problem. In my own code I've tried multiple ways including catching the generic Exception, and I have extensive logging so I'm pretty sure StorageException is the only exception type. – Jamesits Feb 23 '19 at 13:42