Azure Functions error handling and retries
Handling errors in Azure Functions is important to help you avoid lost data, avoid missed events, and monitor the health of your application. It's also an important way to help you understand the retry behaviors of event-based triggers.
This article describes general strategies for error handling and the available retry strategies.
Important
Preview retry policy support for certain triggers was removed in December 2022. Retry policies for supported triggers are now generally available (GA). For a list of extensions that currently support retry policies, see the Retries section.
Handling errors
Errors that occur in an Azure function can come from:
- Use of built-in Functions triggers and bindings.
- Calls to APIs of underlying Azure services.
- Calls to REST endpoints.
- Calls to client libraries, packages, or third-party APIs.
To avoid loss of data or missed messages, it's important to practice good error handling. This table describes some recommended error-handling practices and provides links to more information.
Recommendation | Details |
---|---|
Enable Application Insights | Azure Functions integrates with Application Insights to collect error data, performance data, and runtime logs. You should use Application Insights to discover and better understand errors that occur in your function executions. To learn more, see Monitor Azure Functions. |
Use structured error handling | Capturing and logging errors is critical to monitoring the health of your application. The top-most level of any function code should include a try/catch block. In the catch block, you can capture and log errors. For information about what errors might be raised by bindings, see Binding error codes. Depending on your specific retry strategy, you might also raise a new exception to run the function again. |
Plan your retry strategy | Several Functions bindings extensions provide built-in support for retries and others let you define retry policies, which are implemented by the Functions runtime. For triggers that don't provide retry behaviors, you should consider implementing your own retry scheme. For more information, see Retries. |
Design for idempotency | The occurrence of errors when you're processing data can be a problem for your functions, especially when you're processing messages. It's important to consider what happens when the error occurs and how to avoid duplicate processing. To learn more, see Designing Azure Functions for identical input. |
Retries
There are two kinds of retries available for your functions:
- Built-in retry behaviors of individual trigger extensions
- Retry policies provided by the Functions runtime
The following table indicates which triggers support retries and where the retry behavior is configured. It also links to more information about errors that come from the underlying services.
Trigger/binding | Retry source | Configuration |
---|---|---|
Azure Cosmos DB | Retry policies | Function-level |
Blob Storage | Binding extension | host.json |
Event Grid | Binding extension | Event subscription |
Event Hubs | Retry policies | Function-level |
Kafka | Retry policies | Function-level |
Queue Storage | Binding extension | host.json |
RabbitMQ | Binding extension | Dead letter queue |
Service Bus | Binding extension | host.json* |
Timer | Retry policies | Function-level |
*Requires version 5.x of the Azure Service Bus extension. In older extension versions, retry behaviors are implemented by the Service Bus dead letter queue.
Retry policies
Azure Functions lets you define retry policies for specific trigger types, which are enforced by the runtime. These trigger types currently support retry policies:
Retry support is the same for both v1 and v2 Python programming models.
Retry policies aren't supported in version 1.x of the Functions runtime.
The retry policy tells the runtime to rerun a failed execution until either successful completion occurs or the maximum number of retries is reached.
A retry policy is evaluated when a function executed by a supported trigger type raises an uncaught exception. As a best practice, you should catch all exceptions in your code and raise new exceptions for any errors that you want to result in a retry.
Important
Event Hubs checkpoints aren't written until after the retry policy for the execution has completed. Because of this behavior, progress on the specific partition is paused until the current batch is done processing.
The version 5.x of the Event Hubs extension supports additional retry capabilities for interactions between the Functions host and the event hub. For more information, see clientRetryOptions
in the Event Hubs host.json reference.
Retry strategies
You can configure two retry strategies that are supported by policy:
A specified amount of time is allowed to elapse between each retry.
When running in a Consumption plan, you are only billed for time your function code is executing. You aren't billed for the wait time between executions in either of these retry strategies.
Max retry counts
You can configure the maximum number of times that a function execution is retried before eventual failure. The current retry count is stored in memory of the instance.
It's possible for an instance to have a failure between retry attempts. When an instance fails during a retry policy, the retry count is lost. When there are instance failures, the Event Hubs trigger is able to resume processing and retry the batch on a new instance, with the retry count reset to zero. The timer trigger doesn't resume on a new instance.
This behavior means that the maximum retry count is a best effort. In some rare cases, an execution could be retried more than the requested maximum number of times. For Timer triggers, the retries can be less than the maximum number requested.
Retry examples
Examples are provided for both fixed delay and exponential backoff strategies. To see examples for a specific strategy, you must first select that strategy in the previous tab.
Function-level retries are supported with the following NuGet packages:
- Microsoft.Azure.Functions.Worker.Sdk >= 1.9.0
- Microsoft.Azure.Functions.Worker.Extensions.EventHubs >= 5.2.0
- Microsoft.Azure.Functions.Worker.Extensions.Kafka >= 3.8.0
- Microsoft.Azure.Functions.Worker.Extensions.Timer >= 4.2.0
[Function(nameof(TimerFunction))]
[FixedDelayRetry(5, "00:00:10")]
public static void Run([TimerTrigger("0 */5 * * * *")] TimerInfo timerInfo,
FunctionContext context)
{
var logger = context.GetLogger(nameof(TimerFunction));
logger.LogInformation($"Function Ran. Next timer schedule = {timerInfo.ScheduleStatus.Next}");
}
Property | Description |
---|---|
MaxRetryCount | Required. The maximum number of retries allowed per function execution. -1 means to retry indefinitely. |
DelayInterval | The delay used between retries. Specify it as a string with the format HH:mm:ss . |
Here's an example of a retry policy defined in the function.json file:
{
"disabled": false,
"bindings": [
{
....
}
],
"retry": {
"strategy": "fixedDelay",
"maxRetryCount": 4,
"delayInterval": "00:00:10"
}
}
You can set these properties on retry policy definitions:
Property | Description |
---|---|
strategy | Required. The retry strategy to use. Valid values are fixedDelay or exponentialBackoff . |
maxRetryCount | Required. The maximum number of retries allowed per function execution. -1 means to retry indefinitely. |
delayInterval | The delay used between retries when you're using a fixedDelay strategy. Specify it as a string with the format HH:mm:ss . |
minimumInterval | The minimum retry delay when you're using an exponentialBackoff strategy. Specify it as a string with the format HH:mm:ss . |
maximumInterval | The maximum retry delay when you're using exponentialBackoff strategy. Specify it as a string with the format HH:mm:ss . |
The way you define the retry policy for the trigger depends on your Node.js version.
Here's an example of a Timer trigger function that uses a fixed delay retry strategy:
const { app } = require('@azure/functions');
app.timer('timerTriggerWithRetry', {
schedule: '0 */5 * * * *',
retry: {
strategy: 'fixedDelay',
delayInterval: {
seconds: 10,
},
maxRetryCount: 4,
},
handler: (myTimer, context) => {
if (context.retryContext?.retryCount < 2) {
throw new Error('Retry!');
} else {
context.log('Timer function processed request.');
}
},
});
The way you define the retry policy for the trigger depends on your Node.js version.
Here's an example of a Timer trigger function that uses a fixed delay retry strategy:
import { app, InvocationContext, Timer } from '@azure/functions';
export async function timerTriggerWithRetry(myTimer: Timer, context: InvocationContext): Promise<void> {
if (context.retryContext?.retryCount < 2) {
throw new Error('Retry!');
} else {
context.log('Timer function processed request.');
}
}
app.timer('timerTriggerWithRetry', {
schedule: '0 */5 * * * *',
retry: {
strategy: 'fixedDelay',
delayInterval: {
seconds: 10,
},
maxRetryCount: 4,
},
handler: timerTriggerWithRetry,
});
You can set these properties on retry policy definitions:
Property | Description |
---|---|
strategy | Required. The retry strategy to use. Valid values are fixedDelay or exponentialBackoff . |
maxRetryCount | Required. The maximum number of retries allowed per function execution. -1 means to retry indefinitely. |
delayInterval | The delay used between retries when you're using a fixedDelay strategy. Specify it as a string with the format HH:mm:ss . |
minimumInterval | The minimum retry delay when you're using an exponentialBackoff strategy. Specify it as a string with the format HH:mm:ss . |
maximumInterval | The maximum retry delay when you're using exponentialBackoff strategy. Specify it as a string with the format HH:mm:ss . |
Here's an example of a Timer trigger function that uses a fixed delay retry strategy:
import logging
from azure.functions import AuthLevel, Context, FunctionApp, TimerRequest
app = FunctionApp(http_auth_level=AuthLevel.ANONYMOUS)
@app.timer_trigger(schedule="*/1 * * * * *", arg_name="mytimer",
run_on_startup=False,
use_monitor=False)
@app.retry(strategy="fixed_delay", max_retry_count="3",
delay_interval="00:00:01")
def mytimer(mytimer: TimerRequest, context: Context) -> None:
logging.info(f'Current retry count: {context.retry_context.retry_count}')
if context.retry_context.retry_count == \
context.retry_context.max_retry_count:
logging.info(
f"Max retries of {context.retry_context.max_retry_count} for "
f"function {context.function_name} has been reached")
else:
raise Exception("This is a retryable exception")
You can set these properties on retry policy definitions:
Property | Description |
---|---|
strategy | Required. The retry strategy to use. Valid values are fixed_delay or exponential_backoff . |
max_retry_count | Required. The maximum number of retries allowed per function execution. -1 means to retry indefinitely. |
delay_interval | The delay used between retries when you're using a fixed_delay strategy. Specify it as a string with the format HH:mm:ss . |
minimum_interval | The minimum retry delay when you're using an exponential_backoff strategy. Specify it as a string with the format HH:mm:ss . |
maximum_interval | The maximum retry delay when you're using exponential_backoff strategy. Specify it as a string with the format HH:mm:ss . |
@FunctionName("TimerTriggerJava1")
@FixedDelayRetry(maxRetryCount = 4, delayInterval = "00:00:10")
public void run(
@TimerTrigger(name = "timerInfo", schedule = "0 */5 * * * *") String timerInfo,
final ExecutionContext context
) {
context.getLogger().info("Java Timer trigger function executed at: " + LocalDateTime.now());
}
Binding error codes
When you're integrating with Azure services, errors might originate from the APIs of the underlying services. Information that relates to binding-specific errors is available in the "Exceptions and return codes" sections of the following articles:
- Azure Cosmos DB
- Blob Storage
- Event Grid
- Event Hubs
- IoT Hub
- Notification Hubs
- Queue Storage
- Service Bus
- Table Storage