Azure Logic Apps Error Handling

Try/Catch Scopes and Retry Policies


Introduction

Robust error handling is essential for building reliable Logic Apps workflows. Without proper error handling, failures can cascade through your workflow, leaving messages unprocessed and systems in inconsistent states. Logic Apps provides Try/Catch scopes, retry policies, and run-after configuration to handle failures gracefully and maintain workflow reliability.

This comprehensive guide covers:

  • Scope actions — Grouping actions for error handling
  • Try/Catch pattern — Implementing error detection and handling
  • Run after configuration — Controlling execution based on outcomes
  • Retry policies — Handling transient failures automatically
  • Error output access — Reading error details for logging/alerting
  • Best practices — Building resilient workflows

Understanding Error Flow

Error Propagation in Workflows

┌─────────────────────────────────────────────────────────────────────┐
│                    ERROR PROPAGATION FLOW                           │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │                   NORMAL EXECUTION                          │   │
│   │                                                             │   │
│   │   Action 1 ──▶ Action 2 ──▶ Action 3 ──▶ Success            │   │
│   │                                                             │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │                   WITH ERROR HANDLING                       │   │
│   │                                                             │   │
│   │   ┌─────────┐      ┌─────────┐      ┌─────────┐             │   │
│   │   │  Try    │──▶── │ Success │      │  Catch  │             │   │
│   │   │ Scope   │      │   Path  │      │  Scope  │             │   │
│   │   └─────────┘      └─────────┘      └─────────┘             │   │
│   │        │                │                │                  │   │
│   │        ▼                │                │                  │   │
│   │   Action fails          │                │                  │   │
│   │        │                │                │                  │   │
│   │        └────────────────┘────────────────┘                  │   │
│   │              (Either path executes)                         │   │
│   │                                                             │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Try/Catch Pattern Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                    TRY/CATCH ARCHITECTURE                           │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │                     TRY SCOPE                               │   │
│   │                                                             │   │
│   │   ┌──────────────────────────────────────────────────────┐  │   │
│   │   │  [Action 1] ───▶ [Action 2] ───▶ [Action 3]          │  │   │
│   │   │                                                      │  │   │
│   │   │  If any action fails:                                │  │   │
│   │   │  - Workflow marked as "Failed"                       │  │   │
│   │   │  - Error details captured                            │  │   │
│   │   │  - Execution moves to Catch scope                    │  │   │
│   │   └──────────────────────────────────────────────────────┘  │   │
│   └────────────────────────────┬────────────────────────────────┘   │
│                                │                                    │
│                    ┌───────────┴───────────┐                        │
│                    │   Run After Settings  │                        │
│                    │   - has failed: true  │                        │
│                    └───────────┬───────────┘                        │
│                                │                                    │
│                                ▼                                    │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │                    CATCH SCOPE                              │   │
│   │                                                             │   │
│   │   ┌──────────────────────────────────────────────────────┐  │   │
│   │   │  [Log Error] ───▶ [Send Alert] ───▶ [Clean Up]       │  │   │
│   │   │                                                      │  │   │
│   │   │  Execute error handling actions:                     │  │   │
│   │   │  - Access error details from workflow()              │  │   │
│   │   │  - Send notifications                                │  │   │
│   │   │  - Perform cleanup                                   │  │   │
│   │   │  - Retry if appropriate                              │  │   │
│   │   └──────────────────────────────────────────────────────┘  │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Implementing Try/Catch

Step-by-Step Setup

{
  "definition": {
    "actions": {
      "Try": {
        "type": "Scope",
        "actions": {
          "Get_Order": {
            "type": "ServiceBus",
            "inputs": {
              "method": "get",
              "path": "/queues/orders/messages/dequeue"
            }
          },
          "Process_Order": {
            "type": "Http",
            "inputs": {
              "method": "POST",
              "uri": "https://api.example.com/process",
              "body": "@body('Get_Order')"
            }
          },
          "Complete_Order": {
            "type": "ServiceBus",
            "inputs": {
              "method": "post",
              "path": "/queues/orders/messages/complete",
              "body": {
                "lockToken": "@body('Get_Order')?['LockToken']"
              }
            }
          }
        }
      },
      "Catch": {
        "type": "Scope",
        "runAfter": {
          "Try": {
            "failed": true
          }
        },
        "actions": {
          "Log_Error": {
            "type": "Log",
            "inputs": {
              "message": "@workflow()['errors']"
            }
          },
          "Send_Alert": {
            "type": "SendEmail",
            "inputs": {
              "to": "ops@company.com",
              "subject": "Workflow Failed: Order Processing",
              "body": "@outputs('Try')"
            }
          }
        }
      }
    }
  }
}

Configure Run After

┌─────────────────────────────────────────────────────────────────────┐
│                    RUN AFTER CONFIGURATION                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   Catch Scope → Configure Run After                                 │
│   ─────────────────────────────────────                             │
│                                                                     │
│   ☑ is successful       (optional - handle normal completion)       │
│   ☑ has failed          (required - handle errors)                  │
│   ☑ has timed out      (optional - handle timeouts)                 │
│   ☐ is skipped         (optional - handle skipped actions)          │
│                                                                     │
│   Common configurations:                                            │
│                                                                     │
│   Catch on failure:                                                 │
│   ☑ has failed                                                      │
│                                                                     │
│   Catch on any non-success:                                         │
│   ☐ is successful                                                   │
│   ☑ has failed                                                      │
│   ☑ has timed out                                                   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Handle Different Error Types

Transient vs Permanent Errors

{
  "Handle_transient_errors": {
    "type": "Scope",
    "actions": {
      "Call_API": {
        "type": "Http",
        "inputs": { "uri": "https://api.example.com/data" },
        "runAfter": {}
      }
    }
  },
  "Handle_failure": {
    "type": "Scope",
    "runAfter": {
      "Handle_transient_errors": { "failed": true }
    },
    "actions": {
      "Check_error_type": {
        "type": "Condition",
        "expression": "@contains(toLower(workflow().errors[0].message), 'timeout')",
        "actions": {
          "Retry_later": {
            "type": "Http",
            "inputs": {
              "method": "POST",
              "uri": "https://example.com/retry-queue",
              "body": "@workflow().errors[0]"
            }
          }
        },
        "else": {
          "actions": {
            "Send_alert": {
              "type": "SendEmail",
              "inputs": {
                "subject": "Permanent Failure",
                "body": "@workflow().errors[0].message"
              }
            }
          }
        }
      }
    }
  }
}

HTTP Error Handling

{
  "Http_with_status_handling": {
    "type": "Http",
    "inputs": {
      "method": "GET",
      "uri": "https://api.example.com/data"
    },
    "runAfter": {},
    "runtimeConfiguration": {
      "conditions": [
        {
          "expression": "@equals(outputs('Http_Request')?['statusCode'], 404)",
          "actions": {
            "Handle_NotFound": { "type": "Log", "inputs": { "message": "Not found" } }
          }
        },
        {
          "expression": "@greaterOrEquals(outputs('Http_Request')?['statusCode'], 500)",
          "actions": {
            "Handle_ServerError": { "type": "Log", "inputs": { "message": "Server error" } }
          }
        }
      ]
    }
  }
}

Retry Policies

Configure Retry Options

{
  "retry_policy_examples": {
    "Fixed_retry": {
      "type": "Http",
      "retryPolicy": {
        "type": "fixed",
        "count": 3,
        "interval": "PT5S"
      },
      "inputs": { "uri": "https://api.example.com/data" }
    },
    "Exponential_retry": {
      "type": "Http",
      "retryPolicy": {
        "type": "exponential",
        "count": 4,
        "interval": "PT5S"
      },
      "inputs": { "uri": "https://api.example.com/data" }
    },
    "No_retry": {
      "type": "Http",
      "retryPolicy": {
        "type": "none"
      },
      "inputs": { "uri": "https://api.example.com/data" }
    }
  }
}

Retry Policy Reference

┌─────────────────────────────────────────────────────────────────────┐
│                    RETRY POLICY OPTIONS                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   Type: fixed                                                       │
│   ─────────────────                                                 │
│   Retry at fixed intervals                                          │
│   count: 1-90 (default: 4)                                          │
│   interval: ISO 8601 format (default: PT5S = 5 seconds)             │
│   Example: 5s, 5s, 5s, 5s                                           │
│                                                                     │
│   Type: exponential                                                 │
│   ───────────────────                                               │
│   Retry with exponential backoff                                    │
│   count: 1-90 (default: 4)                                          │
│   interval: ISO 8601 format (default: PT5S = 5 seconds)             │
│   Example: 5s, 10s, 20s, 40s (doubles each time)                    │
│                                                                     │
│   Type: none                                                        │
│   ────────────                                                      │
│   No automatic retries - fail immediately                           │
│   Use for: Permanent failures, idempotent operations                │
│                                                                     │
│   When to Retry:                                                    │
│   ─────────────────                                                 │
│   ✓ Network timeouts                                                │
│   ✓ Service temporarily unavailable                                 │
│   ✓ Rate limiting (429)                                             │
│   ✓ Internal server errors (5xx)                                    │
│                                                                     │
│   Don't Retry:                                                      │
│   ─────────────                                                     │
│   ✗ Authentication failures (401, 403)                              │
│   ✗ Validation errors (4xx except 429)                              │
│   ✗ Resource not found (404)                                        │
│   ✗ Business logic errors                                           │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Service Bus Retry Configuration

{
  "ServiceBus_with_retry": {
    "type": "ServiceBus",
    "retryPolicy": {
      "type": "exponential",
      "count": 3,
      "interval": "PT10S"
    },
    "inputs": {
      "method": "post",
      "path": "/queues/orders/messages"
    }
  }
}

Error Output and Logging

Access Error Details

{
  "Access_error_info": {
    "type": "Compose",
    "inputs": {
      "error_message": "@workflow().errors[0].message",
      "error_code": "@workflow().errors[0].code",
      "error_action": "@workflow().errors[0].actionName",
      "error_timestamp": "@workflow().errors[0].timestamp",
      "all_errors": "@workflow().errors"
    }
  }
}

Comprehensive Error Logging

{
  "Log_error_details": {
    "type": "Compose",
    "inputs": {
      "workflowName": "@workflow().name",
      "workflowRunId": "@workflow().run.id",
      "error": {
        "message": "@workflow().errors[0].message",
        "code": "@workflow().errors[0].code",
        "action": "@workflow().errors[0].actionName",
        "timestamp": "@workflow().errors[0].timestamp"
      },
      "context": {
        "trigger": "@trigger().name",
        "triggerInputs": "@trigger().inputs",
        "retryCount": "@variables('retryCount')"
      }
    }
  }
}

Send Detailed Alert

{
  "Send_error_alert": {
    "type": "SendEmail",
    "inputs": {
      "to": "ops@company.com",
      "subject": "@concat('Workflow Error: ', workflow().name, ' - ', string(utcNow()))",
      "body": "<html><body><h2>Error Details</h2><p><b>Workflow:</b> @{workflow().name}</p><p><b>Run ID:</b> @{workflow().run.id}</p><p><b>Error:</b> @{workflow().errors[0].message}</p><p><b>Action:</b> @{workflow().errors[0].actionName}</p><p><b>Time:</b> @{workflow().errors[0].timestamp}</p></body></html>"
    }
  }
}

Real-World Patterns

Complete Workflow with Error Handling

{
  "Order_processing_workflow": {
    "triggers": {
      "When_order_received": {
        "type": "ServiceBusTrigger",
        "inputs": { "queueName": "orders-queue" }
      }
    },
    "actions": {
      "Try": {
        "type": "Scope",
        "actions": {
          "Parse_Order": {
            "type": "ParseJson",
            "inputs": { "content": "@triggerBody()" }
          },
          "Validate_Order": {
            "type": "Condition",
            "expression": "@not(empty(body('Parse_Order')?['orderId']))",
            "actions": {},
            "else": {
              "actions": {
                "Throw_validation_error": {
                  "type": "Terminate",
                  "inputs": { "statusCode": "BadRequest", "message": "Invalid order" }
                }
              }
            }
          },
          "Process_Order": {
            "type": "Http",
            "inputs": {
              "method": "POST",
              "uri": "https://api.example.com/orders",
              "body": "@body('Parse_Order')"
            },
            "retryPolicy": { "type": "exponential", "count": 3, "interval": "PT5S" }
          },
          "Complete": {
            "type": "ServiceBus",
            "inputs": {
              "method": "post",
              "path": "/queues/orders/messages/complete",
              "body": { "lockToken": "@triggerBody()?['LockToken']" }
            }
          }
        }
      },
      "Catch": {
        "type": "Scope",
        "runAfter": {
          "Try": { "failed": true }
        },
        "actions": {
          "Log_error": {
            "type": "Compose",
            "inputs": {
              "error": "@workflow().errors[0]",
              "order": "@triggerBody()",
              "timestamp": "@utcNow()"
            }
          },
          "Save_to_error_queue": {
            "type": "ServiceBus",
            "inputs": {
              "method": "post",
              "path": "/queues/error-orders/messages",
              "body": "@outputs('Log_error')"
            }
          },
          "Send_alert": {
            "type": "SendEmail",
            "inputs": {
              "to": "ops@company.com",
              "subject": "Order Processing Failed",
              "body": "@outputs('Log_error')"
            }
          }
        }
      }
    }
  }
}

Nested Error Handling

{
  "Nested_try_catch": {
    "actions": {
      "Outer_Try": {
        "type": "Scope",
        "actions": {
          "Inner_Try": {
            "type": "Scope",
            "actions": {
              "Risky_Operation": { "type": "Http" }
            },
            "runAfter": {}
          },
          "Success_Action": {
            "type": "Log",
            "inputs": { "message": "Operation succeeded" },
            "runAfter": { "Inner_Try": ["Succeeded"] }
          }
        }
      },
      "Outer_Catch": {
        "type": "Scope",
        "runAfter": {
          "Outer_Try": { "failed": true }
        },
        "actions": {
          "Handle_error": { "type": "SendEmail" }
        }
      }
    }
  }
}

Best Practices

Error Handling Checklist

PracticeDescription
Always use Try/CatchWrap critical operations in scopes
Configure run afterSet proper failure conditions
Add retry policiesHandle transient failures
Log error detailsCapture for debugging
Send notificationsAlert on failures
Clean up resourcesRelease locks, close connections

Design Patterns

┌─────────────────────────────────────────────────────────────────────┐
│                 ERROR HANDLING BEST PRACTICES                       │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   1. SCOPE CRITICAL OPERATIONS                                      │
│      └─ Group related actions in single scope                       │
│      └─ Don't try/catch individual small actions                    │
│                                                                     │
│   2. HANDLE SPECIFIC ERRORS                                         │
│      └─ Check error type before taking action                       │
│      └─ Retry transient, alert on permanent                         │
│                                                                     │
│   3. LOG EVERYTHING                                                 │
│      └─ Save error details to storage for analysis                  │
│      └─ Include context: workflow name, run ID, inputs              │
│                                                                     │
│   4. NOTIFY APPROPRIATELY                                           │
│      └─ Critical failures → immediate alert                         │
│      └─ Non-critical → batched notifications                        │
│                                                                     │
│   5. CLEAN UP RESOURCES                                             │
│      └─ Always complete or dead-letter Service Bus messages         │
│      └─ Release any locks or temporary resources                    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Related Topics


Azure Integration Hub - Intermediate Level