How to get the Error Message with Logic App Try-Catch (Part II) – Using an Azure Function

In the first part of this blog post: How to get the Error Message with Logic App Try-Catch (Part I), I mention that when we are creating more enterprise processes, the business process will typically not be simple without, for example, conditions or nested conditions, cycles, switch and so on. They will be more complicated, and we usually have nested conditions, conditions inside the loops, and so on. In that case, the approach explained in the first blog post will not work. For example, in this scenario:

We would expect, and we want to get the error where indeed happen – marked in the arrow on the picture. Instead, however, what we get is the top-level action description marked with the square in the picture, saying:

  • ActionFailed. An action failed. No dependent actions succeeded.

Of course, this is not what we intend for. So, what are my alternatives? How do I get the correct error message?

You have at least to options on the table to address and solve this issue:

  • Using a code-first approach by using an Azure Function – this is the approach we will be explaining here today.
  • and using a no-code low-code approach by using a Logic App (could be a child Logic App or global Logic App in order for you not to implement the same logic in all your workflows) – this approach we will be explaining in a future blog post.

My dear friend Mike Stephenson wrote in the past a blog post, and when I spoke with him and shared some ideas about this topic, he pointed out his blog post: Cleaner Error Message with Logic App Try-Catch.

Globally, his approach works like this:

  • For this problem, he will implement a try/catch pattern in the usual way, but in the catch block, he will go to call a function and pass the id for the run of the logic app.
  • In the function, he will then call the Azure Management REST API and use a filter to get the actions that have a failed status.
  • He will then loop through them (the errors), but he will remove the ones where the message is “An action failed. No dependent actions succeeded” because these are usually scope shapes with an error inside them and are just noise. And we want just the actual errors.
  • For each action that is a genuine error, he will then use the properties to get the link to the message body and call it to get the underlying error message from the action.
  • In the end, he will then return an array of the errors within the Logic App run.

My version works with the same principles as Mike’s version. The only differences are that my version:

  • Is prepared to be global across resources, subscriptions, and Logic Apps. That means that we will be passing not only the run id but optionally, we can pass the:
    • Subscription Id
    • Resource name
    • Logic App name
  • And there are several improvements (I hope) in the code like:
    • Error handling
    • Different scenarios or actions may have different behaviors:
      • Some of them don’t have an action[“properties”][“error”][“message”], and in these cases we need to invoke the action[“properties”][“outputsLink”][“uri”] to get the correct error message.
      • In some cases, we don’t need to perform an additional call because the error message is already present in the action[“properties”][“error”][“message”]
    • and so on.

ExtractLogicAppError Azure Function

You can download the complete code for the function from GitHub. The link is below at the bottom of the blog post. As Mike’s sample, this function will also need to use a managed identity in order to call the Azure management REST API:

string logicAppHistory = System.Environment.GetEnvironmentVariable("logicAppHistory", EnvironmentVariableTarget.Process);
string defaultSubscriptionId = System.Environment.GetEnvironmentVariable("defaultSubscriptionId", EnvironmentVariableTarget.Process);
string defaultResourceGroup = System.Environment.GetEnvironmentVariable("defaultResourceGroup", EnvironmentVariableTarget.Process);

#region Set Management Azure Logic App Historic URL 

if (string.IsNullOrEmpty(data.SubscriptionId))
    logicAppHistory = logicAppHistory.Replace("{subId}", defaultSubscriptionId);
logicAppHistory = logicAppHistory.Replace("{subId}", data.SubscriptionId);

if (string.IsNullOrEmpty(data.ResourceGroup))
    logicAppHistory = logicAppHistory.Replace("{rgName}", defaultResourceGroup);
logicAppHistory = logicAppHistory.Replace("{rgName}", data.ResourceGroup);

logicAppHistory = logicAppHistory.Replace("{laName}", data.LogicAppName);
logicAppHistory = logicAppHistory.Replace("{runId}", data.Runid);



var target = "";
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(target);

var logicAppRunRequest = new HttpRequestMessage(HttpMethod.Get, logicAppHistory);
logicAppRunRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var result = await _client.SendAsync(logicAppRunRequest);

This is important to know because we need to configure that properly to be able to call the REST API and read the errors.

The response will be composed of the name of the action, the type of the action, status (always Failed), code, the start time, the end time, and the error message:

var item = new JObject();
item.Add(new JProperty("name", action["name"]));
item.Add(new JProperty("type", action["type"]));
item.Add(new JProperty("status", action["properties"]["status"]));
item.Add(new JProperty("code", action["properties"]["code"]));
item.Add(new JProperty("startTime", action["properties"]["startTime"]));
item.Add(new JProperty("endTime", action["properties"]["endTime"]));
item.Add(new JProperty("errorMessage", action["properties"]["error"]["message"]));

A sample of the Function response will be something like:

    "name": "Get_secret",
    "type": "Microsoft.Logic/workflows/runs/actions",
    "status": "Failed",
    "code": "NotFound",
    "startTime": "2022-06-27T16:45:44.9740069Z",
    "endTime": "2022-06-27T16:45:45.1927622Z",
    "errorMessage": "{\"statusCode\":404,\"headers\":{\"Pragma\":\"no-cache\",\"x-ms-request-id\":\"fac7e84f-91ce-43bf-bc8c-d8e3e73c7c3b\",\"Strict-Transport-Security\":\"max-age=31536000; includeSubDomains\",\"X-Content-Type-Options\":\"nosniff\",\"X-Frame-Options\":\"DENY\",\"Cache-Control\":\"no-store, no-cache\",\"Set-Cookie\":\"ARRAffinity=f84ed6eb5a002caxxxxxxf9b20db;Path=/;HttpOnly;Secure;,ARRAffinitySameSite=f84ed6exxxxxxxxx33f221df9b20db;Path=/;HttpOnly;SameSite=None;Secure;\",\"Timing-Allow-Origin\":\"*\",\"x-ms-apihub-cached-response\":\"false\",\"x-ms-apihub-obo\":\"true\",\"Date\":\"Mon, 27 Jun 2022 16:45:45 GMT\",\"Content-Length\":\"355\",\"Content-Type\":\"application/json\",\"Expires\":\"-1\"},\"body\":{\"status\":404,\"message\":\"Operation failed because the requested resource was not found in the key vault.\\r\\nclientRequestId: fac7e84f-91ce-43bf-bc8c-d8e3e73c7c3b\",\"error\":{\"message\":\"Operation failed because the requested resource was not found in the key vault.\"},\"source\":\"\"}}"

Configure Function App managed identity and permissions

After you deploy the Azure Function, we need to:

  • First, configure the function app with a system-assigned managed identity.
    • On the Azure Function app, access to Identity option under the Settings region.
    • On the System assigned tab, set the status to On.
    • finally, click Save.
  • Second, in each resource group that the function needs to access to read the Logic App run history, we need to give that managed identity the Logic App Operator role.

For this last step:

  • On the Azure Portal, access the Resource Group that has our Logic App from where we want to grab the correct error message.
  • On the Resource Group page, select the option Access control (IAM).
  • on the Access control (IAM) panel, click Add > Add role assignment
  • On the Add role assignment page, on the Role tab, search for Logic App and then select the option Logic App Operator, and then click Next.
  • On the Members tab, select the Assign access to be Managed identity, and from the Members:
    • Select your subscription on the subscription property.
    • On the Managed identity list of options, select the Function App option
    • and on the Select property, select the managed identity of our function and then click Close.
  • Click on Review + Assign and then Review + Assign again.

We can now use this Function inside our Logic Apps.

Logic App Implementation

In my catch block, we can now call the previous function we created, and we will pass all the required fields necessary for the Function to work. This will pass the subscription, resource group, workflow name, and run id as a string to the function.

In the Catch section (Scope) and configure the Azure Function properly, as you see in the picture below:

We can then send it back or log the full error descriptions of the failures. To get all the valid error descriptions, we can basically set a supporting variable with:

  • @{body(‘ExtractLogicAppError’)}

Or we want to extract the first error message detail.

  • first(body(‘ExtractLogicAppError’))?[‘errorMessage’]

And now you can try your business processes.

Where I can download it

You can download the complete Azure Function source code here:

Stay tuned because I will explain how to get the correct error message using the Logic App in the third part of this blog post.

Author: Sandro Pereira

Sandro Pereira lives in Portugal and works as a consultant at DevScope. In the past years, he has been working on implementing Integration scenarios both on-premises and cloud for various clients, each with different scenarios from a technical point of view, size, and criticality, using Microsoft Azure, Microsoft BizTalk Server and different technologies like AS2, EDI, RosettaNet, SAP, TIBCO etc. He is a regular blogger, international speaker, and technical reviewer of several BizTalk books all focused on Integration. He is also the author of the book “BizTalk Mapping Patterns & Best Practices”. He has been awarded MVP since 2011 for his contributions to the integration community.

Leave a Reply

Your email address will not be published.

BizTalk Server

Over 650+ customers across
30+ countries depend on BizTalk360

Learn More

Operate efficiently with enterprise-grade Azure monitoring,
tracing, remediation & governance in one platform

Learn More

Back to Top