What Happens to a Failed Asynchronous Lambda Invocation - Caller Retries, the Internal Queue, and Where a Discarded Event Goes

First Published:
Last Updated:

When an AWS Lambda function is invoked asynchronously and the invocation fails, the handling of the event – who retries it, where it is discarded, and where records are kept – changes before and after Lambda returns an HTTP 202 response. Before the 202 response, the caller is responsible. AWS SDK, Amazon S3, Amazon SNS, and Amazon EventBridge each retry the Invoke request according to their own rules. SNS and EventBridge document where events go after their retries run out, but no statement was found on where S3 sends them. After the 202 response is returned, the Lambda internal queue takes over. The internal queue retries function errors by default up to 2 times, and throttles or system errors by default up to a maximum of 6 hours, before discarding the event. Before discarding, if configured, it may send invocation records to a destination or the event payload to a dead-letter queue. If no configuration is present, the event is lost without a trace. If sending to the dead-letter queue fails, Lambda deletes the event; whether a destination configured alongside the queue still receives a record is No statement found (Section 6.6).

This article lays out the two layers in a table of callers and layers and in two figures. The callers consist of five elements: SDK and AWS CLI with InvocationType=Event, S3 event notifications, SNS, the EventBridge Custom event bus - Classic, and the new EventBridge Custom event bus (added in September 2026). For each layer, the article covers how retries are classified, the maximum event age, what destinations and dead-letter queues keep, the cases where nothing remains, and what the metrics show. It distinguishes between what is documented in official materials and what was not found within those materials. The article also includes observations from creating functions and queues in us-east-1, in Chapter 10. It did not test invocations from S3, SNS, or EventBridge.

This article is not a step-by-step guide for configuring asynchronous invocations, nor does it cover pricing. The information presented was verified as of November 5, 2026.

Related articles on this site:

Table of Contents

  1. 1. Scope and Assumptions
  2. 2. How to Read the Tables
  3. 3. The Two Layers — Before and After the 202
  4. 4. Callers and Layers
  5. 5. Retries in the Internal Queue
  6. 6. What Remains — Destinations and Dead-Letter Queues
  7. 7. When No Trace Remains
  8. 8. What the Metrics Reveal
  9. 9. Where the Sources Disagree and What Is Not Written
  10. 10. What This Setup Showed
  11. 11. Failure Modes
  12. 12. Frequently Asked Questions About Failed Asynchronous Invocations
  13. 13. Summary
  14. 14. References

1. Scope and Assumptions

This chapter will first define the intended audience, the scope of this article, the relevant source materials, and the terminology used. The conclusions come first, as the following ten points.

  • Asynchronous invocations are divided into two layers. This article refers to retries performed before Lambda returns a 202 from Invoke as Layer A, and retries performed after the 202 response as Layer B. The caller manages Layer A, while Lambda's internal queue manages Layer B (Chapter 3).
  • The retry behavior and destination for Layer A vary depending on the caller. The EventBridge defaults depend on the bus: 24 hours and 185 attempts on the Custom event bus - Classic, and 5 attempts or 300 seconds, whichever comes first, on the new Custom event bus. According to the SNS pages, SNS retries for 23 days, 100,015 times. No statement was found on where events go when S3 cannot invoke Lambda; the Lambda FAQ says only that S3 may retain and retry events rejected by Lambda for 24 hours (Chapter 4).
  • Lambda's retry behavior for Layer B is consistent regardless of the caller, as described on the retry overview page. Function errors are retried twice by default, while throttling and system errors are retried for up to 6 hours by default. For functions with a reserved concurrency of 0, Lambda sends new events directly to the dead-letter queue or the on-failure destination, without attempting any retries (Chapter 5).
  • Layer B has two configuration settings: MaximumRetryAttempts (0 to 2) and MaximumEventAgeInSeconds (60 to 21600). MaximumRetryAttempts only applies to retries for function errors. PutFunctionEventInvokeConfig overwrites the existing configuration and removes any settings it does not specify (Section 5.6).
  • Invocation records are delivered to the destination. The dead-letter queue receives the event payload with attributes, but does not include details of the function's response (Chapter 6).
  • According to the API reference, both the destination and the dead-letter queue can be configured. However, no statement was found in the current documentation that failed events are delivered to both. There is only a sentence in a 2019 AWS Compute Blog post (Section 6.6).
  • In some cases, failed events may not be retained in either the destination or the dead-letter queue. This occurs when there is no destination or dead-letter queue configured, or when sending to the dead-letter queue fails because of permissions or size limits (whether a destination configured alongside the queue still receives a record is No statement found (Section 6.6)). No statement was found on whether events remain when delivery to a destination fails or when the function cannot keep up with the queue (Chapter 7).
  • AsyncEventsDropped does not represent the number of lost events. According to the metrics page, if a destination or dead-letter queue is configured, events are sent to that location and then discarded. In this article's reading, to know whether an event was lost, you also need to look at DeadLetterErrors and DestinationDeliveryFailures (Chapter 8).
  • Lambda's documentation presents two different descriptions regarding the internal queue. The Lambda SNS page states that asynchronous invocations process each event at least once. Conversely, the Invoke API reference indicates that events may be lost if the function cannot keep up with the queue (Section 9.1).
  • In the configuration described in Chapter 10, for functions with both a destination and a dead-letter queue configured, failed events were delivered to both in the cases of function errors, passing the maximum event age, and a reserved concurrency of 0. The condition in the destination records was RetriesExhausted, EventAgeExceeded, and ZeroReservedConcurrency for each case. The event with a maximum event age of 60 seconds was sent to both right after the first failure, without waiting for a retry. Each is a single observation. (Chapter 10)

1.1 Intended Audience

This article is intended for developers and operations personnel who are currently using a configuration that asynchronously invokes Lambda functions from S3, SNS, EventBridge, and SDKs. In particular, it is written for readers who believe any of the following:

  • Failed asynchronous invocations will always leave an event in either the destination or a dead-letter queue.
  • Lambda retries a failed asynchronous invocation three times.
  • EventBridge will retry invoking a Lambda function up to 185 times within a 24-hour period.
  • If both a destination and a dead-letter queue are configured, failed events will reach both.
  • If the AsyncEventsDropped metric is not zero, it indicates that events are being lost.

Each of these either holds in some cases and not in others, depending on the layer, the caller, the error type, and the bus type, or cannot be confirmed in the documentation.

1.2 Scope: What This Article Covers, and What It Does Not

This article focuses on the handling, retry, discarding, and records of events when an asynchronous invocation fails. Specifically, it addresses the following:

  • The two-layer structure split at the 202.
  • Layer A retries for the five callers, and where events go when they run out.
  • Classification, intervals, and two distinct settings for retries in Layer B.
  • What destinations and dead-letter queues keep, the units they are configured at, and what happens when both are configured.
  • The cases where nothing remains, and where events stopped by recursive loop detection go.
  • Metrics related to asynchronous invocations.
  • Where the sources disagree, and statements that were not found.

This article does not cover:

  • Pricing and billing mechanisms.
  • Failures when reading from event sources via Event Source Mappings (ESM). A related article, What Happens to a Failed Batch in a Lambda Event Source Mapping, covers this. This article takes over the handling of asynchronous invocation retries that were excluded in that article's Section 1.2.
  • Concurrency and throttling mechanisms. This site's AWS Lambda Concurrency and Scaling Guide addresses this. This article only covers retry behavior for throttled invocations and the handling of a reserved concurrency setting of 0.
  • The overall differences between two EventBridge buses. This site's Amazon EventBridge Custom Event Bus and the Classic Bus covers this. This article only addresses the default settings when invoking Lambda.
  • Service-specific ordering guarantees. This site's What Breaks Ordering, Service by Service on AWS covers this.
  • Anti-patterns in event-driven architectures that involve recursive loops. This site's Event-Driven Architecture Anti-Patterns on AWS covers this. This article only addresses the detection of recursive loops and the destination of events that are stopped due to such detection. It does not describe the steps that trigger the loop.
  • Handling of Lambda target failures with EventBridge Pipes and delivery guarantees with EventBridge Scheduler. This article only aims to differentiate between similarly named configurations.
  • Lambda durable functions. The retry and discard mechanisms described in this article are distinct from the retry mechanisms for steps within durable functions.
  • Lambda Web Functions and Lambda MicroVMs.
  • General principles of designing idempotent processing.

1.3 Date of Review and Materials Reviewed

This article was written after reviewing the following materials on November 5, 2026. Abbreviations enclosed in parentheses will be used throughout the text.

  • In the AWS Lambda Developer Guide: the page on asynchronous invocation entry points (the asynchronous invocation page), the page on error handling (the error handling page), the page on error handling configuration (the configuration page), and the page on recording invocations (the records page).
  • In the AWS Lambda Developer Guide: the page on retry behavior overview (the retry overview page), the page on metric types (the metrics page), the page on recursive loop detection (the recursive loop page), the page on quotas (the quotas page), and the page on runtimes (the runtimes page).
  • In the AWS Lambda Developer Guide: the page on using Lambda with S3 (the Lambda S3 page), and the page on using Lambda with SNS (the Lambda SNS page).
  • In the AWS Lambda API Reference: the pages for Invoke, PutFunctionEventInvokeConfig, UpdateFunctionEventInvokeConfig, and DestinationConfig (collectively referred to as the API Reference; individually, they are referred to as the API Reference for Invoke, for example).
  • In the Amazon EventBridge User Guide: the page on the new Custom event bus (the new bus page), the page on its Lambda target (the Lambda target page), the page on its retry and dead-letter queue behavior (the new bus retry page), the page on retry behavior for the Custom event bus - Classic (the Classic retry page), and the page on the dead-letter queue for Classic (the Classic DLQ page).
  • In the Amazon EventBridge API Reference and the EventBridge Scheduler API Reference: the page for RetryPolicy (read only for distinction within Section 5.7).
  • In the Amazon SNS Developer Guide: the page on message retry behavior (the SNS retry page), the page on the dead-letter queue (the SNS DLQ page), and the page on large messages (the SNS large message page).
  • In the Amazon S3 User Guide: the page on event notifications, and the page on event types and destinations (collectively referred to as the S3 event notification pages).
  • In the Amazon SQS Developer Guide: the page on message quotas (the SQS quotas page).
  • In the AWS SDKs and Tools Reference Guide: the page on retry behavior (the SDK retry page).
  • Six announcements in AWS What's New: an announcement from November 20, 2024, on using S3 as a failed-event destination; an announcement from October 24, 2025, regarding increasing the asynchronous invocation limit to 1 MB; an announcement from September 18, 2026, regarding SNS 1 MiB; an announcement from September 24, 2026, regarding EventBridge buses; an announcement from October 12, 2026, regarding Lambda Web Functions; and an announcement from October 14, 2026, regarding expanding recursive loop detection to EventBridge custom event buses. The dates correspond to the postDateTime value in the announcements.
  • In the AWS Compute Blog: an article from 2019 introducing Lambda destinations (the 2019 destinations blog post).
  • Read on November 6, 2026: the AWS Lambda FAQ (the Lambda FAQ) and the Amazon SNS FAQ (the SNS FAQ). Neither is part of the developer guides.

1.4 Terms, and Different Things With the Same Name

This article uses the following terms consistently:

  • Asynchronous invocation: An invocation where the caller does not wait for the function's response. In the SDK and AWS CLI, this is specified by setting InvocationType to Event.
  • Internal queue: A queue where Lambda stores events for asynchronous invocations. This is separate from an Amazon SQS queue. In this article, both Lambda's asynchronous event queue and asynchronous invocation queue are referred to as the internal queue.
  • Layer A and Layer B: These are terms used in this article and do not appear in the official documentation. Layer A refers to the retries performed by the caller after initiating an Invoke call, until Lambda returns a 202 status code. Layer B refers to the retries that occur after the 202 status code, when the internal queue invokes the function.
  • Retry: This article specifies who is performing the retry each time. The caller performs retries in Layer A, while Lambda performs retries in Layer B.
  • Discard: The process by which Lambda removes events from the internal queue after all retries have been exhausted or the event has passed its maximum age. Before discarding, the event may be sent to a destination or a dead-letter queue.
  • Destination: A location configured using DestinationConfig. There are two types: a destination for successful invocations (on-success) and a destination for failed invocations (on-failure). This receives invocation records.
  • Dead-letter queue: A location configured using the function's DeadLetterConfig. This receives the event payload itself. This article distinguishes it from the dead-letter queues of SNS subscriptions, EventBridge targets and subscribers, and SQS redrive policies, and says which one it means each time. Specifically, events sent to a dead-letter queue on a new EventBridge bus are not the event itself, but rather a record of the failure (see Section 4.6).
  • Invocation record: A JSON document delivered to the destination. It contains details of the request and response (see Section 6.2).
  • Function name: The Lambda runtimes page calls the existing Lambda functions Lambda Event Functions and says the following. All functions in this article are Event Functions. The text calls them Lambda functions, and quotations keep the original wording.

AWS Lambda Event Functions (previously known as Lambda functions) support multiple languages through the use of runtimes.

  • MaximumEventAgeInSeconds: The same parameter name exists in the settings for Lambda's asynchronous invocations, EventBridge Classic targets, and the EventBridge Scheduler. Their ranges and default values differ (see Section 5.7).

2. How to Read the Tables

This chapter defines the columns in the tables presented in this article, as well as how to describe the condition of the materials and how to cite the sources. The three articles in the same series share the columns of the summary table in Section 4.7.

2.1 Table Columns

The summary table in Section 4.7 has the following seven columns:

  • Layer: The layer referenced in that row.
  • What This Layer Keeps: What information that layer holds regarding events.
  • What Can Be Lost Here: What might be lost within that layer.
  • Set By: The settings that determine the behavior of that layer, and the entity that owns those settings.
  • How You Can Tell: How to determine what has occurred, and where to verify it.
  • Where It Is Written: The origin of the information in that row.
  • Source State: The state of the sources, described with the terms in Section 2.2.

The table from Section 4.1, the table classifying retries from Section 5.1, and the table from Section 6.1 regarding destinations and dead-letter queues use columns specific to this article. Each section explains its columns.

2.2 Terms for the State of the Sources

This article uses eight terms to describe the state of the documentation. These English terms will also be used in the tables.

  • Stated: The documentation contains a statement regarding the matter.
  • Stated as not supported: The documentation explicitly states that the matter is not supported or cannot be used.
  • Documentation does not list: While a list or table exists, the specific item is not included. This does not necessarily indicate that the matter is unsupported or unavailable.
  • Not recorded (stated): The documentation states that the information is not included in logs or records.
  • No statement found: Despite searching the materials in Section 1.3 with different search terms and different kinds of documentation, no relevant statement could be found. This does not necessarily indicate that the matter is absent or does not occur.
  • Sources disagree: The statements within two different documents regarding the same matter are contradictory. This article presents both statements, along with their sources, without designating either as incorrect.
  • Inference: This is an inference made by this article, and is not a statement found within any of the documentation. This article says in the same sentence that it is an inference.
  • Observation: This refers to findings observed during testing (as described in Chapter 10), and does not represent a statement from the documentation itself. This article does not overwrite documentation statements with observations.

This article distinguishes between "Stated as not supported" and "No statement found." The former refers to a statement in the documentation, while the latter indicates that this article was unable to find a relevant statement.

2.3 Citing Sources

In this article, for each point, the page containing the relevant text is indicated in the same sentence. Even when discussing Lambda, this article treats text from the developer guide, API reference, and EventBridge user guide as separate sources. For example, the sentence stating that you can configure both a destination and a dead-letter queue refers to the API reference, not the developer guide. The sentence that calls the internal queue at-least-once is a sentence on the EventBridge Lambda target page. The sentence that says asynchronous invocations process each event at least once is a sentence on the Lambda SNS page (Section 5.5). For sentences from AWS blogs, this article names the blog in the same sentence.

3. The Two Layers — Before and After the 202

This chapter explains why asynchronous invocations are divided into two layers, using sentences from the documentation.

3.1 The 202 Is the Boundary

The asynchronous invocation page describes asynchronous invocation as follows:

For asynchronous invocation, Lambda places the event in a queue and returns a success response without additional information. A separate process reads events from the queue and sends them to your function.

According to the Invoke API reference, when InvocationType is Event, the status code for a successful response is 202. The asynchronous invocation page says the following about events that cannot be placed in the queue:

If Lambda isn't able to add the event to the queue, the error message appears in the command output.

From these two sentences, this article reads that the event owner transitions when a 202 status code is received. If Invoke fails before receiving a 202, the event is not in the internal queue, and it is the caller's responsibility to retry the event. After receiving a 202, the event is in the internal queue, and Lambda is responsible for sending it to the function and retrying if necessary. This article refers to the former as Layer A and the latter as Layer B.

3.2 The End of Layer A Depends on the Caller's Settings

The Lambda target page for the new EventBridge Custom event bus describes the two values of InvocationType as follows:

EVENT invokes asynchronously and treats a successful Invoke as delivered. REQUEST_RESPONSE waits for the function to return and treats a function error as a failed delivery. After a successful EVENT invoke, delivery to the function's code is governed by Lambda's asynchronous queue, which is at-least-once.

When set to EVENT, EventBridge considers the delivery complete as soon as the Invoke call is successful. After this, it proceeds to Layer B. When set to REQUEST_RESPONSE, EventBridge waits for the function to return and treats any function errors as a delivery failure. In this case, in this article's reading, the invocation is synchronous, and there is no Layer B. Function error retries fall under EventBridge's retry policy (Layer A). The same page also specifies the wait time for EventBridge, InvocationTimeoutSeconds, which can be set from 1 to 30 seconds, with a default value of 30 seconds. No statement giving the default value of InvocationType was found on either the Lambda target page or in the description of LambdaParameters in the new bus API.

The at-least-once in the quotation above is a word from the EventBridge Lambda target page. How Lambda documents its internal queue behavior will be discussed in Sections 5.5 and 9.1.

3.3 Potential for Double Retries

Layers A and B each have their own retry mechanisms. In this article's reading, whether a particular failure is retried on Layer A or Layer B depends on whether the error occurred before or after the 202 response.

The Invoke API reference lists potential errors returned by Invoke itself, including TooManyRequestsException (HTTP 429 – exceeding the request throughput limit), ServiceException (HTTP 500 – an internal Lambda error), and RequestTooLargeException (HTTP 413 – exceeding the request body size limit). The caller handles these errors, which occur before the 202 response, and determines whether to retry.

In contrast, the error handling page describes throttling (429) and system errors (500-series) when Lambda invokes the function from the internal queue as follows:

For throttling errors (429) and system errors (500-series), Lambda returns the event to the queue and attempts to run the function again for up to 6 hours by default.

Even when the error is the same 429, a 429 returned by Invoke will trigger a retry on Layer A, while a 429 in the internal queue will trigger a retry on Layer B. The Invoke API reference also states that TooManyRequestsException is returned when the concurrent execution limit is exceeded. However, it does not specify whether this applies to asynchronous invocations. The error handling page describes throttling during asynchronous invocations, when concurrent execution is insufficient, as a retry in the internal queue. The quotas page states that the maximum number of requests for asynchronous invocations is determined solely by the concurrent execution capacity of the function.

Figure 1 illustrates the two-layered structure.

Two Layers of Retries in an Asynchronous Lambda Invocation
Two Layers of Retries in an Asynchronous Lambda Invocation

4. Callers and Layers

This chapter lists the retry attempts for Layer A, along with the destination after a failure, for the five callers. The retry overview page says that Layer B operates under the same rules regardless of the caller.

For asynchronous invocation, the retry logic is the same regardless of the invocation source.

4.1 The Caller Table

The following table uses columns specific to this article. Caller indicates the caller. Layer A Retries defines the rules by which the caller will retry the Invoke operation. When Layer A Ends specifies the point at which the caller considers the delivery complete. Where Layer A Gives Up indicates the destination for events when Layer A's retry attempts are exhausted. Layer B indicates whether that event is then directed to Layer B.

CallerLayer A RetriesWhen Layer A EndsWhere Layer A Gives UpLayer BWhere It Is WrittenSource State
SDK and AWS CLI with InvocationType=EventSDK retry settings. The default on the SDK retry page is standard mode, with max_attempts set to 3 (initial request plus a maximum of 2 retries). However, this page's behavior is enabled with AWS_NEW_RETRIES_2026=true.When Invoke returns 202An error is returned to the calling code. No record remains on the Lambda side.EntersInvoke API reference, asynchronous invocation page, SDK retry pageStated. That no record remains on the Lambda side is Inference.
S3 Event NotificationsThe S3 event notification page states that it is designed to deliver notifications at least once. It mentions S3's retry mechanism but does not specify the number of attempts or duration. The Lambda FAQ says that S3 may retain and retry events rejected by Lambda for 24 hours.According to the Lambda S3 page, S3 invokes the function asynchronously.No statement foundEntersS3 event notification page, Lambda S3 page, Lambda FAQDuration for events Lambda rejects: Stated (Lambda FAQ). Number and destination: No statement found.
SNSThe SNS retry page indicates that Lambda is listed under AWS managed endpoints, with 100,015 attempts over 23 days. The Lambda SNS page states that it retries for several hours.According to the Lambda SNS page, Lambda processes SNS messages asynchronously.Subscription dead-letter queue (SQS queue). If none exists, it is discarded.EntersSNS retry page, SNS DLQ page, Lambda SNS pageDuration: Sources disagree.
EventBridge Custom event bus - ClassicRetry policy for the target. The default is 24 hours and 185 attempts.What counts as success for a Lambda target: No statement found on the Classic pages.Target dead-letter queue (standard SQS queue). If none exists, it is discarded.Enters (Inference)Classic retry page, Classic DLQ page, announcement from October 24, 2025What constitutes success: No statement found.
New EventBridge Custom event busSubscriber's RetryPolicy. The default is either 5 attempts or 300 seconds, whichever comes first.For EVENT, a successful Invoke. For REQUEST_RESPONSE, a successful function.Failure records are sent to the SQS queue specified in the subscriber's OnFailureConfiguration. If none exists, it is discarded.Enters only for EVENT.Lambda target page, new bus retry pageStated. Default InvocationType: Documentation does not list

4.2 SDK and AWS CLI

When using the SDK or AWS CLI and specifying Event for the InvocationType, the SDK's retry settings determine retry behavior for Layer A. The SDK retry page indicates a default value of 3 for max_attempts, and states:

A max attempts value of 3 means the SDK makes one initial request and up to two retries.

The same page also lists TooManyRequestsException as an error that will be retried with a longer wait time (throttling). However, at the beginning of this page, the following note is provided:

The behavior described on this page requires opting in until it becomes the default behavior. Set AWS_NEW_RETRIES_2026=true in your environment.

If this setting is not configured, the SDK will use retry behavior prior to 2026, which differs in wait times, retry quota costs, and service-specific defaults, according to the same page. This article has not checked the pre-2026 defaults of each SDK.

When the SDK exhausts its retry attempts, the error is returned to the calling code. The API reference for PutFunctionEventInvokeConfig describes the DestinationConfig parameter as specifying the destination for the event after it is sent to the function.

A destination for events after they have been sent to a function for processing.

In this article's reading, an event for which no 202 was received is not in the internal queue and reaches neither a Lambda destination nor the dead-letter queue. The only remaining clue is the error received by the calling code.

4.3 S3 Event Notifications

The Lambda S3 page states that S3 invokes the function asynchronously. The S3 event notification page describes how notifications are delivered as follows:

Amazon S3 event notifications are designed to be delivered at least once. Typically, event notifications are delivered in seconds but can sometimes take a minute or longer.

Another section of the S3 event notification page explains that the order in which notifications are received may not be the same as the order in which the events occurred. It also notes that, in rare cases, S3's retry mechanism may result in duplicate notifications for the same event.

On rare occasions, Amazon S3's retry mechanism might cause duplicate S3 Event Notifications for the same object event.

No statement was found describing how many times S3 retries calling a Lambda function, the duration of those retries, or where failed notifications are sent. This article searched three times, varying the search terms, on the S3 event notification pages, the Lambda S3 page, and a re:Post article addressing troubleshooting when S3 fails to invoke Lambda. One sentence outside these pages covers part of the question. In its answer on exceeding the concurrent executions limit, the Lambda FAQ says:

In case the Lambda function is being invoked in response to Amazon S3 events, events rejected by AWS Lambda may be retained and retried by S3 for 24 hours.

This sentence covers only events that Lambda rejects as throttled, and it states neither the number of attempts nor where the events go after 24 hours. Layer A for S3 is therefore Stated only for that case and that duration (Lambda FAQ); the number of attempts and the destination are No statement found.

4.4 SNS

The Lambda SNS page describes invocations from SNS as follows:

Lambda processes SNS messages asynchronously by queuing the messages and handling retries. If Amazon SNS can't reach Lambda or the message is rejected, Amazon SNS retries at increasing intervals over several hours.

The SNS retry page outlines the delivery policy, placing AWS Lambda and Amazon SQS in the row for AWS managed endpoints. This row specifies three attempts with no delay, two attempts every one second, ten attempts with exponential backoff between one and twenty seconds, and one hundred thousand attempts every twenty seconds, for a total as follows:

100,015 times, over 23 days

The SNS DLQ page also states that, for server-side errors from AWS managed endpoints backed by Lambda and SQS, SNS retries up to 100,015 times over 23 days. The several hours on the Lambda SNS page and the 23-day period described on the SNS page do not align. This discrepancy is addressed in Section 9.1, and neither is identified as an error.

According to the SNS DLQ page, SNS will not retry in the event of client-side errors, such as when a subscribed Lambda function is deleted or when a function's policy no longer permits SNS delivery. When retries are exhausted or in the event of a client-side error, SNS will discard the message. However, if the subscription includes a dead-letter queue, the message will be sent there.

When the delivery policy is exhausted, Amazon SNS stops retrying the delivery and discards the message—unless a dead-letter queue is attached to the subscription.

The SNS dead-letter queue is an SQS queue, and the SNS DLQ page states that it must be in the same AWS account and Region as the subscription. This is distinct from the dead-letter queue for Lambda functions, as described in Chapter 6.

4.5 The EventBridge Custom Event Bus - Classic

The Classic retry page describes retries to the targets of a rule as follows:

By default, EventBridge retries sending the event for 24 hours and up to 185 times with an exponential back off and jitter, or randomized delay.

The same page describes events whose retry attempts are exhausted:

If an event isn't delivered after all retry attempts are exhausted, the event is dropped and EventBridge doesn't continue to process it.

According to the Classic DLQ page, the target's dead-letter queue is a standard SQS queue, and messages within it include the attributes RULE_ARN, TARGET_ARN, and ERROR_CODE. The page states that in cases of errors such as insufficient permissions to reach the target or the target no longer existing, EventBridge will not attempt retries; the page says that some events are dropped or sent to a dead-letter queue without any retry attempts.

The two Classic pages do not specify how Classic determines success when targeting Lambda functions. The announcement dated October 24, 2025, lists EventBridge as an example of a service that starts asynchronous invocations. This article reads that a Classic rule invokes a Lambda function asynchronously and ends Layer A at the 202. This is an inference.

4.6 The New EventBridge Custom Event Bus

EventBridge announced on September 24, 2026, the addition of a new Custom event bus that utilizes subscribers instead of rules. The new bus page calls the existing bus Custom Event Bus - Classic and says that both remain available.

EventBridge also provides Custom Event Bus - Classic, the event bus that uses rules and targets. The two products share the events: IAM namespace and the events.amazonaws.com service principal, and both remain available.

The new bus retry page describes subscriber retries as follows.

When a target call fails, EventBridge retries it until one of the two limits in RetryPolicy is reached, by default 5 attempts or 300 seconds, then writes a record to the Amazon SQS queue in OnFailureConfiguration.Arn.

The range for MaxRetryAttempts is 0 to 185, with a default value of 5. The range for MaxEventAgeInSeconds is 60 to 86,400, with a default value of 300. The same page states that delivery stops at whichever limit is reached first. If a dead-letter queue is not configured, events that have exhausted all retry attempts will be discarded, and the only available clue will be the EventsDropped metric. The same page also states that if the delivery role cannot write a failure record to the dead-letter queue, the OnFailureDestinationFailed metric counts each record that could not be written, and those events are lost.

The dead-letter queue for the new bus receives records, not events.

The record is a JSON document, not the event.

These records contain the following information: errorCode, errorMessage, exhaustedRetryCondition, retryAttempts, and failedMessages. The errorCode values include COMPUTE_EXECUTION_ERROR, which occurs when a function returns an error with the REQUEST_RESPONSE setting. The new bus retry page also says that EventBridge retries failures inside EventBridge itself without limit and never produces a record for them.

Failures inside EventBridge itself are retried by EventBridge without limit and never produce a record.

For the new bus, the Lambda target's InvocationType determines the end of Layer A (see Section 3.2). When set to EVENT, Layer A ends at the 202, and the event proceeds to Layer B. When set to REQUEST_RESPONSE, any errors from the function are included in the retry process for Layer A, and there is no Layer B. The Lambda target page recommends using REQUEST_RESPONSE when using FIFO subscribers to ensure events are processed in order.

A comprehensive overview of the differences in default settings between the two bus types can be found on this site's Amazon EventBridge Custom Event Bus and the Classic Bus.

4.7 Summary Table

The following table lists the layers of an asynchronous invocation, with the columns shared by the three articles in the same series.

LayerWhat This Layer KeepsWhat Can Be Lost HereSet ByHow You Can TellWhere It Is WrittenSource State
Caller (Layer A)Events occurring before receiving a 202 response.Events whose caller retries run out. In SNS and EventBridge, events are discarded if there is no dead-letter queue.Caller's retry settings. SDK configuration, SNS delivery policy, EventBridge RetryPolicy.Error in the calling code, caller's dead-letter queue, EventsDropped.Sections 4.2 through 4.6.S3: duration for events Lambda rejects is Stated (Lambda FAQ); number and destination are No statement found.
Lambda internal queue (Layer B)Events occurring after a 202 response. Events remain in the internal queue during retry attempts.Events discarded after exhausting retry attempts or passing the maximum event age. Events can disappear without being sent to the function if the function cannot keep up with the queue.Function, version, alias: MaximumRetryAttempts and MaximumEventAgeInSeconds.AsyncEventsReceived, AsyncEventAge, AsyncEventsDropped.Error handling page, Invoke API reference, metrics page.Stated.
On-success and on-failure destinationsInvocation records. In the event of a failure, it includes details of the request and response.Records that fail to be sent, and how the event is then handled.Function, version, alias: DestinationConfig.Records delivered to the destination, DestinationDeliveryFailures.Records page, API reference, metrics page.No statement found regarding handling after delivery failure.
Dead-letter queue on the functionThe event payload, along with the RequestID, ErrorCode, and ErrorMessage attributes.Events that fail to be sent. Lambda deletes the event.Function's DeadLetterConfig. Function level only.Messages delivered to the queue, DeadLetterErrors.Records page, metrics page.Stated.

5. Retries in the Internal Queue

This chapter covers how Layer B retries are classified, their intervals, and the two settings.

5.1 Retry Classification Table

The following table uses columns specific to this article. Cause indicates the reason for failure when the function is invoked from the internal queue. What Lambda Does describes the action Lambda takes in that situation. Interval specifies the retry interval, and Limited By indicates the settings that control when retries are stopped.

CauseWhat Lambda DoesIntervalLimited ByWhere It Is WrittenSource State
Function Error (errors returned by the code, or runtime errors such as timeouts)By default, attempts to execute the function two more times.Between the first and second attempts: 1 minute. Between the second and third attempts: 2 minutes.MaximumRetryAttempts (0-2) and MaximumEventAgeInSecondsError Handling page, API ReferenceStated
Throttling (429)Returns the event to the queue and attempts to execute the function again for up to 6 hours by default.Exponentially increasing, from 1 second to a maximum of 5 minutes.MaximumEventAgeInSeconds (60-21600)Error Handling pageStated
System Error (500 series)Same as Throttling.Same as Throttling.MaximumEventAgeInSecondsError Handling pageStated
Reserved concurrency is 0Sends new events to the dead-letter queue or the on-failure destination, without attempting retries.——Records pageStated
Excessive Queue EntriesIncreases the retry interval and reduces the rate at which events are read from the queue.Increases. How far: No statement foundMaximumEventAgeInSecondsError Handling pageStated

5.2 Function Errors

The error handling page describes retries for function errors as follows:

If the function returns an error, by default Lambda attempts to run it two more times, with a one-minute wait between the first two attempts, and two minutes between the second and third attempts.

According to the same page, function errors include errors returned by the function's code and errors returned by the runtime, such as timeouts. The default retry count of 2 applies only to function errors. When combined with the initial execution, this results in a total of 3 attempts, but this count does not apply to throttling or system errors.

According to the API reference, MaximumRetryAttempts specifies the maximum number of times a function will be retried when it returns an error, and its range is 0 to 2. Setting it to 0 means that no retries will be attempted after a function error.

5.3 Throttling and System Errors

In the case of throttling (429) and system errors (500-series), Lambda will return the event to the queue and attempt to re-execute the function, up to a default maximum of 6 hours (see the quotation in Section 3.3). The error handling page specifies the interval as:

The retry interval increases exponentially from 1 second after the first attempt to a maximum of 5 minutes.

This article reads that these retries are not counted towards the MaximumRetryAttempts value. This is because the description of MaximumRetryAttempts is limited to function errors, while the error handling page specifies that retries for throttling and system errors are measured in time, rather than the number of attempts. The mechanism for stopping these retries for throttling and system errors is MaximumEventAgeInSeconds. The default of 6 hours is the same duration as the 6 hours mentioned in the following sentence in the API reference.

It retains events in a queue for up to six hours.

5.4 Functions With Reserved Concurrency Set to 0

The records page describes functions with reserved concurrency set to 0 as follows:

When you set reserved concurrency to zero for an asynchronously invoked function, Lambda begins sending new events to the configured dead-letter queue or the on-failure event destination, without any retries.

According to the same page, to process events received while reserved concurrency is 0, you must consume them from the dead-letter queue or the on-failure destination. The metrics page defines AsyncEventsDropped, listing reserved concurrency of 0 as one reason for dropping events (see Section 8.3). According to that same definition, if a destination or dead-letter queue is available, the event is sent to it before being dropped, so in this article's reading, if neither is available, the event does not remain.

5.5 Long Queues, Duplicates, and Order

When the queue contains many entries, the error handling page states that Lambda will increase the retry interval and reduce the rate at which it reads from the queue. The same page also notes that when the queue is very long, new events may pass their maximum age before they are sent to the function.

When the queue is very long, new events might age out before Lambda has a chance to send them to your Event Function.

The same page further explains that even if the function does not return an error, the same event may be delivered multiple times, attributing this to the nature of the internal queue.

Even if your Event Function doesn't return an error, it's possible for it to receive the same event from Lambda multiple times because the queue itself is eventually consistent.

The Lambda SNS page states that asynchronous invocations process each event at least once, and that duplicates may occur. As Section 2.3 says, the at-least-once sentence quoted in Section 3.2 is a sentence from the EventBridge Lambda target page, while this sentence is from a Lambda page.

No statement was found explicitly guaranteeing that the internal queue delivers events to the function in the order they are received. This article reviewed the asynchronous invocation page, the error handling page, and the Invoke API reference. No statement indicating that order is not guaranteed was found either. This article can only state that no guarantee regarding order is written. The EventBridge Lambda target page mentions that if processing occurs in order using FIFO subscribers, the REQUEST_RESPONSE option should be used, as described in Section 4.6.

5.6 The Two Settings and Where They Apply

The configuration page describes the two Layer B settings as follows:

MaximumEventAgeInSeconds: The maximum amount of time, in seconds, that Lambda keeps an event in the asynchronous event queue before discarding it.

MaximumRetryAttempts: The maximum number of times that Lambda retries events when the function returns an error.

The API reference range specifies that MaximumEventAgeInSeconds is between 60 and 21600, and MaximumRetryAttempts is between 0 and 2. The console description states that the maximum event age is up to 6 hours, and the number of retries is between 0 and 2.

The configuration page and the records page state that these two settings and destinations can be configured on a function, a version, or an alias. The dead-letter queue alone can be added or removed only at the function level (see Section 6.5).

The configuration APIs include two options: overwriting and partial updates. The API reference for PutFunctionEventInvokeConfig says:

If a configuration already exists for a function, version, or alias, this operation overwrites it. If you exclude any settings, they are removed. To set one option without affecting existing settings for other options, use UpdateFunctionEventInvokeConfig.

For example, if you call PutFunctionEventInvokeConfig on a function with only the MaximumRetryAttempts parameter specified, the current destination configuration is removed, in this article's reading. When modifying only one configuration, use UpdateFunctionEventInvokeConfig.

5.7 Different Settings With the Same Name

The names MaximumEventAgeInSeconds and MaximumRetryAttempts are also used in the RetryPolicy for EventBridge Classic targets, as well as in the RetryPolicy for EventBridge Scheduler. The new bus's RetryPolicy uses shorter names that begin with Max: MaxEventAgeInSeconds and MaxRetryAttempts. The following table lists the respective ranges and default values.

SettingWhereRangeDefaultWhat It LimitsWhere It Is Written
MaximumRetryAttemptsLambda asynchronous invocations0–22The number of times the function is invoked from the internal queue after a function error.API Reference
MaximumEventAgeInSecondsLambda asynchronous invocations60–216006 hoursThe amount of time an event remains in the internal queue.API Reference
MaximumRetryAttempts and MaximumEventAgeInSecondsEventBridge Classic target RetryPolicy0–185 and 60–86400185 attempts and 24 hoursThe number of times and duration for which EventBridge retries delivering to a target.EventBridge API Reference, Classic retry page
MaximumRetryAttempts and MaximumEventAgeInSecondsEventBridge Scheduler RetryPolicy0–185 and 60–86400(Not checked by this article)The number of times and duration for which the Scheduler retries invoking a target.EventBridge Scheduler API Reference
MaxRetryAttempts and MaxEventAgeInSecondsEventBridge new bus subscriber RetryPolicy0–185 and 60–864005 attempts and 300 secondsThe number of times and duration for which EventBridge retries delivering to a subscriber's target.New bus retry page

In this article's reading, when an EventBridge Classic rule or Scheduler invokes a Lambda function, the EventBridge settings apply to Layer A and the Lambda settings apply to Layer B. Even if settings have the same name, changing one does not affect the other. The Scheduler's retry behavior is covered in this site's Amazon EventBridge Scheduler Delivery Semantics.

6. What Remains — Destinations and Dead-Letter Queues

This chapter covers what remains in destinations and dead-letter queues for the events that Layer B discards.

6.1 Destination and Dead-Letter Queue Table

The following table uses columns specific to this article. Item refers to the element being compared, On-Failure Destination indicates the destination in case of failure, and Dead-Letter Queue refers to the function's dead-letter queue.

ItemOn-Failure DestinationDead-Letter QueueWhere It Is Written
Received DataInvocation record (JSON). Contains details of the request and response, along with the reason it was sent.The original event body, as is. Does not include response details. Includes the attributes RequestID, ErrorCode, and ErrorMessage.Records page
Sending ConditionsWhen all processing attempts fail, or when the maximum event age is exceeded.When all processing attempts fail, or when the event expires before being processed.Records page
SuccessA separate destination can be configured for successful processing.None.Records page
Destination TypeStandard SQS queue, standard SNS topic, S3 bucket (only in case of failure), Lambda function, EventBridge event bus.Standard SQS queue, standard SNS topic.Records page, API Reference
FIFOSQS FIFO queues and SNS FIFO topics are unsupported types, and delivery to them fails.FIFO queues and SNS FIFO topics are not supported.Records page, Metrics page
Configuration ScopeFunction, version, alias.Only the function. Versions use the configuration for the unpublished version ($LATEST).Records page
Failure to SendDestinationDeliveryFailures is generated. The handling of the event: No statement foundThe event is deleted, and DeadLetterErrors is generated.Records page, Metrics page

6.2 Destination and Invocation Records

The records page describes what a destination receives as follows:

The invocation record contains details about the request and response in JSON format.

The records page says that the on-failure destination receives a record under the following condition:

On failure – Send a record when the event fails all processing attempts or exceeds the maximum age.

The records page shows the following example of a record for an event where a function experienced an error and failed three processing attempts:

{
  "version": "1.0",
  "timestamp": "2019-11-14T18:16:05.568Z",
  "requestContext": {
    "requestId": "e4b46cbf-b738-xmpl-8880-a18cdf61200e",
    "functionArn": "arn:aws:lambda:us-east-1:123456789012:function:my-function:$LATEST",
    "condition": "RetriesExhausted",
    "approximateInvokeCount": 3
  },
  "requestPayload": {
    "ORDER_IDS": [
      "9e07af03-ce31-4ff3-xmpl-36dce652cb4f",
      "637de236-e7b2-464e-xmpl-baf57f86bb53",
      "a81ddca6-2c35-45c7-xmpl-c3a03a31ed15"
    ]
  },
  "responseContext": {
    "statusCode": 200,
    "executedVersion": "$LATEST",
    "functionError": "Unhandled"
  },
  "responsePayload": {
    "errorMessage": "RequestId: e4b46cbf-b738-xmpl-8880-a18cdf61200e Process exited before completing request"
  }
}

requestPayload is the event passed to the function. requestContext.condition indicates the reason for the record; in this example, it is RetriesExhausted, signifying that all retry attempts have been used. approximateInvokeCount is the approximate number of times the function was invoked. responseContext and responsePayload represent the response from the final attempt.

Neither the records page nor the API reference lists the possible values of condition. The asynchronous invocation pages do not list the value of condition when the event passes its maximum age, when reserved concurrency is 0, or when the invocation is successful. A blog post from 2019 regarding destinations provides an example of a successful record with "condition": "Success". In the setup in Chapter 10, four values were observed: RetriesExhausted, EventAgeExceeded, ZeroReservedConcurrency, and Success. This is based on observation.

6.3 Delivery Methods Based on Destination Type

The records page has a table of the required permissions and how each destination type receives the record. This section summarizes that table.

  • SQS Queue: Requires the sqs:SendMessage permission. Records are delivered as messages (Message).
  • SNS Topic: Requires the sns:Publish permission. Records are delivered as messages.
  • S3 Bucket (in case of failure only): Requires the s3:PutObject and s3:ListBucket permissions. Records are placed as JSON objects with the following name: aws/lambda/async/<function-name>/YYYY/MM/DD/YYYY-MM-DDTHH.MM.SS-<Random UUID>.
  • Lambda Function: Requires the lambda:InvokeFunction permission. Records are delivered as input to the function.
  • EventBridge Event Bus: Requires the events:PutEvents permission. Records are delivered in the detail field of the event. The source field is lambda, and the detail-type field is either Lambda Function Invocation Result - Success or Lambda Function Invocation Result - Failure.

Since November 20, 2024, S3 buckets can be selected as an on-failure destination. The records page warns that if you delete an S3 bucket configured as a destination while still associated with a function, another user in a different account could create a bucket with the same name and potentially receive those failed records. The same page recommends restricting the execution role's s3:PutObject permission to only apply to buckets within your own account.

6.4 The Dead-Letter Queue and Its Attributes

The records page describes what a dead-letter queue receives, compared with a destination, as follows:

For dead-letter queues, Lambda only sends the content of the event, without details about the response.

Lambda sends the event to the dead-letter queue as-is, with additional information in attributes.

The same page lists three attributes:

  • RequestID (String): The ID of the request made to the function. This will appear in the function's logs.
  • ErrorCode (Number): The HTTP status code.
  • ErrorMessage (String): The first 1 KB of the error message.

The records page also says that events in the dead-letter queue can be reprocessed by setting the queue as an event source for the function or by retrieving them manually. In this article's reading, because the dead-letter queue has no function response, you investigate the cause of the failure from the first 1 KB of ErrorMessage and from the function's logs, looked up with RequestID.

6.5 Configuration Units

Destinations can be configured on a function, a version, or an alias. Dead-letter queues can only be configured on a function. The records page says:

However, you can only add or remove a dead-letter queue at the function level. Event Function versions use the same dead-letter queue settings as the unpublished version ($LATEST).

Dead-letter queues are configured through the DeadLetterConfig setting, and can be modified using UpdateFunctionConfiguration. Destinations, along with the two settings described in Section 5.6, can be modified using PutFunctionEventInvokeConfig and UpdateFunctionEventInvokeConfig. These are separate APIs.

6.6 When Both a Destination and a Dead-Letter Queue Are Configured

The ability to configure both a destination and a dead-letter queue is documented in the API reference for PutFunctionEventInvokeConfig, not in the developer guide.

You can configure destinations in addition to or instead of a dead-letter queue.

No statement was found in the current documentation that explicitly states whether failed events are delivered to both the destination and the dead-letter queue, or to only one of them. This article reviewed the records page, the configuration page, the error handling page, the API reference, and the metrics page.

A blog post from 2019 regarding destinations states the following:

If both Destinations and DLQ are used for Failure notifications, function invoke errors are sent to both DLQ and Destinations targets.

This statement comes from the 2019 blog post and is not a statement found in the current developer guide. It is also limited to errors related to function invocations. The blog post does not address whether events that expire or events triggered when the reserved concurrency is 0 are delivered to both locations. This article does not write delivery to both as a promise of the documentation. In the setup in Chapter 10, events from function errors, from passing the maximum event age, and from reserved concurrency of 0 were all delivered to both (Sections 10.2 through 10.5). This is based on a single observation.

Figure 2 illustrates what remains after a failed event.

What Remains After an Asynchronous Event Fails
What Remains After an Asynchronous Event Fails

7. When No Trace Remains

This chapter outlines scenarios where events that fail on Layer B leave no trace in either the destination or the dead-letter queue, and also addresses situations where it is impossible to determine whether a trace remains through available documentation. For events lost on Layer A, where they go is described for each caller in Chapter 4.

7.1 No Destination and No Dead-Letter Queue

In functions without a destination or dead-letter queue, events discarded by Layer B will not be retained. The PutFunctionEventInvokeConfig API reference states:

When an event fails all processing attempts or stays in the asynchronous invocation queue for too long, Lambda discards it. To retain discarded events, configure a dead-letter queue with UpdateFunctionConfiguration.

The error handling page and the configuration page also state that Lambda discards expired events and events that fail all attempts. With the default settings, there is no destination and no dead-letter queue.

7.2 Sending Fails

When sending to a dead-letter queue fails, Lambda deletes the event. The records page says:

If Lambda can't send a message to the dead-letter queue, it deletes the event and emits the DeadLetterErrors metric.

The same page lists potential causes as insufficient permissions and a message size exceeding the limits for the queue or topic.

The records page and the metrics page also state that when sending to a destination fails, Lambda emits a DestinationDeliveryFailures metric. Potential causes listed include incompatible queue or topic types (such as SQS FIFO queues or SNS FIFO topics), permission issues, and exceeding size limits. No statement was found on what happens to the event in such cases, for example, whether it goes to the dead-letter queue.

7.3 Size

The maximum input size for asynchronous invocations is 1 MB. The Invoke API reference states:

For synchronous invocations, the maximum payload size is 6 MB. For asynchronous invocations, the maximum payload size is 1 MB.

The limit was increased from 256 KB to 1 MB in the announcement dated October 24, 2025.

What is sent to destinations and dead-letter queues is larger than the input. The invocation record includes the entire input plus the response and metadata, while dead-letter queue messages consist of the body plus attributes. The records page warns about SNS destinations:

When using Amazon SNS as a destination, be aware that Amazon SNS has a maximum message size limit of 256 KB.

The same warning says that if the input is close to 1 MB, the record may exceed the SNS limit and cause delivery failures, and recommends considering SQS or S3 destinations for larger payloads.

However, the SNS large message page states that, since the announcement dated September 18, 2026, SNS can accept messages up to 1 MiB through the topic's MaximumMessageSize attribute. The default remains 256 KiB.

Amazon SNS supports message payloads up to 1 MiB (1,048,576 bytes) when you configure the MaximumMessageSize topic attribute. By default, topics accept messages up to 256 KiB.

The 256 KB on the records page and the 1 MiB on the SNS page do not align, depending on the topic configuration. This article puts this in Section 9.1. No statement was found on whether large records arrive when a topic with a raised MaximumMessageSize is a Lambda destination. According to the SQS quotas page, the maximum size for SQS messages is 1 MiB.

The records page gives an example of the same kind for dead-letter queues. If a function experiences an error when processing an SNS notification with a body size close to 1 MB, the event data added by SNS and the attributes added by Lambda may cause the message to exceed the dead-letter queue limit. In such cases, as described in Section 7.2, Lambda will delete the event.

7.4 The Function Cannot Keep Up With the Queue

The Invoke API reference states the following about asynchronous invocation:

If your function does not have enough capacity to keep up with the queue, events may be lost.

The error handling page describes the same situation as follows:

If the function can't keep up with incoming events, events might also be deleted from the queue without being sent to the function.

Later in the same paragraph, the Invoke API reference indicates that a dead-letter queue should be configured to retain unprocessed events.

To retain events that were not processed, configure your function with a dead-letter queue.

However, it does not explicitly state whether events that disappear because the function fails to keep up with the queue will be included in these unprocessed events. No statement was found that specifies whether such lost events are sent to a destination or a dead-letter queue. The error handling page later notes that when the queue becomes very long, new events may expire, and expired events are discarded (see Section 7.1). According to the records page, expired events are subject to both destinations and dead-letter queues. This article cannot determine whether events that disappear without being sent to the function and events that expire and are subsequently discarded are the same.

7.5 Events Stopped by Recursive Loop Detection

When a function is invoked many times in the same chain of requests, Lambda stops it as a recursive loop. The recursive loop page describes what it detects as follows:

Lambda currently detects recursive loops between your functions, Amazon SQS, Amazon S3, Amazon SNS, and Amazon EventBridge Custom event buses. Lambda also detects loops comprised only of Lambda Event Functions, which might invoke each other synchronously or asynchronously.

EventBridge Custom event buses were added with the announcement dated October 14, 2026. The announcement mentions misconfigured EventBridge rules invoking functions recursively. This article reads that the Classic bus, which uses rules, is covered. This is an inference. No statement was found on whether loops through subscribers on the new bus are covered.

According to the recursive loop page, when a function is invoked approximately 16 times in the same chain, Lambda stops the next invocation in that chain. The same page describes where the stopped event goes as follows:

If you have an on-failure destination or dead-letter queue configured for your Event Function, then Lambda also sends the event from the stopped invocation to your destination or dead-letter queue.

No statement was found on what happens to a stopped event when there is no destination or dead-letter queue. In this article's reading, as in Section 7.1, the event does not remain. The same page cautions that using the same resource for the function's trigger and destination or dead-letter queue can create another loop.

There are limitations to the detection process. According to the recursive loop page, loops involving services outside of the list, such as DynamoDB, cannot be detected. Detection relies on tracing the chain using metadata attached by the corresponding AWS SDK, so the function must use a supported SDK. Detection can be disabled on a per-function basis, and a loop through a function with detection turned off is not stopped. Design flaws involving recursive loops are discussed in this site's Event-Driven Architecture Anti-Patterns on AWS.

8. What the Metrics Reveal

This chapter lists the metrics that show what happened in Layer B.

8.1 Metrics Table

The following table summarizes the definitions on the metrics page.

MetricWhat It CountsWhere It Is Written
AsyncEventsReceivedThe number of events successfully placed in the queue by Lambda.Metrics page
AsyncEventAgeThe time elapsed between an event being placed in the queue and the function being invoked. Increases when failures or throttling result in retries.Metrics page
AsyncEventsDroppedThe number of events dropped without successfully executing the function. If there is a destination or dead-letter queue, events are sent there before being dropped.Metrics page
DeadLetterErrorsThe number of times sending to the dead-letter queue failed.Metrics page
DestinationDeliveryFailuresThe number of times sending to the destination failed.Metrics page
RecursiveInvocationsDroppedThe number of times recursive invocations were stopped. This metric will not appear for functions where recursion detection is disabled.Metrics page
InvocationsThe number of times the function's code was invoked. Includes both successful invocations and function errors, but does not include throttled requests.Metrics page
ErrorsThe number of invocations that resulted in a function error.Metrics page
ThrottlesThe number of throttled requests.Metrics page

8.2 The Difference Between AsyncEventsReceived and Invocations

The metrics page describes the difference between these two metrics as follows:

Mismatches between AsyncEventsReceived and Invocations can indicate a disparity in processing, events being dropped, or a potential queue backlog.

In this article's reading, Invocations increases each time the function's code is called, including retries, while AsyncEventsReceived counts each event only once. If this interpretation is correct, Invocations will be higher when a function error triggers a retry, while AsyncEventsReceived will be higher when throttling or discarding events occurs. The metrics page does not state that AsyncEventsReceived does not count retries. The values in Section 10.7 are consistent with this interpretation.

8.3 AsyncEventsDropped Is Not a Count of Lost Events

The definition of AsyncEventsDropped includes the following statement:

If you configure a dead-letter queue (DLQ) or OnFailure destination, then events are sent there before they're dropped.

AsyncEventsDropped includes events that were dropped after being sent to either the destination or the dead-letter queue. The same definition lists reasons for dropping events, such as exceeding the maximum event age, exhausting the maximum retry attempts, or reserved concurrency set to 0. In this article's reading, to know which events remained nowhere, you need to consider AsyncEventsDropped in conjunction with DeadLetterErrors and DestinationDeliveryFailures, and also check whether the destination and dead-letter queue are available. No statement was found on whether the events in Section 7.4 that disappear without being sent to the function are counted in AsyncEventsDropped.

8.4 Metrics on the Caller Side

Events lost in Layer A will not appear in Lambda's metrics. This is because, in this article's reading, the AsyncEventsReceived metric only counts events that were successfully placed in the queue. On the caller side, the following metrics are available:

  • The new EventBridge bus: If all retry attempts have been exhausted and there is no dead-letter queue, the only indication is the EventsDropped metric, as noted on the new bus retry page. If the failure record cannot be written to the dead-letter queue, the same page names the OnFailureDestinationFailed metric and says those events are lost.
  • EventBridge Classic: If a dead-letter queue is used, the InvocationsSentToDLQ and InvocationsFailedToBeSentToDLQ metrics will be available, as described on the Classic DLQ page.

Metrics related to SNS and S3 are outside the scope of this article.

9. Where the Sources Disagree and What Is Not Written

This chapter gathers the items in the states Sources disagree, Documentation does not list, and No statement found from Section 2.2.

9.1 Where the Sources Disagree

  • Retry Period for SNS Delivery to Lambda: The Lambda SNS page states that SNS will retry delivery for several hours. The SNS retry page and the SNS DLQ (Dead-Letter Queue) page state that this occurs 100,015 times over a period of 23 days (Section 4.4).
  • SNS Message Size Limit: The records page indicates that the SNS message size limit is 256 KB. The SNS large message page states that it can be raised to 1 MiB with the MaximumMessageSize topic attribute (Section 7.3).
  • Internal Queue Processing of Events: The Lambda SNS page states that asynchronous invocations process each event at least once. The Invoke API reference states that events may be lost if the function cannot keep up with the queue, and the error handling page states that events may be deleted from the queue without being sent to the function (Sections 5.5 and 7.4). This article presents both statements as they appear in the documentation and does not definitively identify either as incorrect.
  • Retries of Function Errors: The Lambda FAQ states that asynchronously invoked functions "are retried at least 3 times." The retry overview page and the error handling page state that function errors are retried up to 2 times by default (Section 5.1).
  • Throttled Asynchronous Events: The Lambda FAQ states that asynchronous invocations "can absorb reasonable bursts of traffic for approximately 15-30 minutes, after which incoming events will be rejected as throttled." The error handling page states that throttling is retried in the internal queue for up to 6 hours by default (Sections 3.3 and 5.1). The SNS FAQ states that if Lambda throttles an SNS message, SNS retries the delivery.

9.2 What the Documentation Does Not List

  • A list of possible values for requestContext.condition in invocation records (see Section 6.2). Four values were observed in the setup in Chapter 10.
  • The default InvocationType for a new EventBridge bus's Lambda target (see Section 3.2).
  • The defaults of the retry behavior before 2026, on the SDK retry page (see Section 4.2).

9.3 Statements Not Found

  • When both a destination and a dead-letter queue are configured, does the event reach both? Only a sentence in the 2019 destinations blog post exists (Section 6.6).
  • When S3 cannot invoke a Lambda function, how many times does S3 retry, for how long outside the case in the Lambda FAQ (24 hours for events Lambda rejects), and where do the notifications go? (Section 4.3)
  • Does the internal queue guarantee order? No statement that it does not was found either. (Section 5.5)
  • How is the event handled when sending to a destination fails? (Section 7.2)
  • If an event is removed from the queue without being sent to a function, is it sent to the destination or the dead-letter queue? (Section 7.4)
  • What happens to an event that is stopped due to a recursive loop, when neither a destination nor a dead-letter queue is configured? (Section 7.5)
  • What does an EventBridge Classic rule treat as a successful delivery to a Lambda target? (Section 4.5)
  • Does the recursive loop detection apply to loops that pass through a new EventBridge bus subscriber? (Section 7.5)
  • Does the sentence in the Invoke API reference that TooManyRequestsException is returned at a concurrency limit also apply to asynchronous invocations? (Section 3.3; the FAQs in Section 9.1 describe throttled rejections, but the API reference does not)

10. What This Setup Showed

This chapter details the observations made while monitoring the behavior of Layer B in the setup deployed in us-east-1. The information presented here is an observation as described in Section 2.2 and does not constitute a promise of the documentation. A single observation does not necessarily indicate consistent behavior.

10.1 Setup

The setup was built with a single AWS CloudFormation stack.

  • One Lambda function. The runtime is Python 3.14, the architecture is arm64, the memory is 128 MB, and the timeout is 10 seconds. If the input contains "succeed": true, the function succeeds and returns a response; otherwise, it throws an exception. On each attempt, the function writes the input case and the time to its log.
  • Three standard SQS queues, used as the on-failure destination, the on-success destination, and the function's dead-letter queue.
  • The function's dead-letter queue is configured using the DeadLetterConfig setting of the function, and is set to the third queue.
  • Invocations were made from the AWS CLI on a local machine with InvocationType set to Event. The input is a JSON payload of a few dozen bytes.

The following five test cases were performed:

  • C1: With the default settings (2 retry attempts, a maximum event age of 21600 seconds), the on-success and on-failure destinations and the dead-letter queue were all configured. One failing event and one succeeding event were sent.
  • C2: Using UpdateFunctionEventInvokeConfig, MaximumRetryAttempts was set to 0, and a failing event was sent.
  • C3: MaximumRetryAttempts was set back to 2, and MaximumEventAgeInSeconds was set to 60, and a failing event was sent.
  • C4: The maximum event age was set back to 21600 seconds, the reserved concurrency was set to 0, and an event that was expected to succeed was sent.
  • C5: PutFunctionEventInvokeConfig was called, specifying only MaximumRetryAttempts for the function with a destination configured. No event was sent.

The setup was created on November 5, 2026. The timestamps for each case are recorded in UTC.

10.2 C1 — Default Settings With Both a Destination and a Dead-Letter Queue

An event that fails and an event that succeeds were sent at 08:04:42 and 08:04:43. Invoke returned 202 in both cases.

The failing event was executed three times, according to the function logs. The interval between the first and second execution was approximately 64 seconds, and the interval between the second and third execution was approximately 109 seconds. These are close to the 1 minute and 2 minutes on the error handling page, but not the same. All three executions of the failing event had the same log request ID.

After the third failure, a message was delivered to both the on-failure destination queue and the dead-letter queue. The difference in the SentTimestamp from SQS was 26 milliseconds. The invocation record delivered to the destination is as follows. The account ID and request ID have been redacted.

{
  "version": "1.0",
  "timestamp": "2026-11-05T08:07:37.027Z",
  "requestContext": {
    "requestId": "<request-id>",
    "functionArn": "<redacted>",
    "condition": "RetriesExhausted",
    "approximateInvokeCount": 3
  },
  "requestPayload": {
    "case": "C1-fail"
  },
  "responseContext": {
    "statusCode": 200,
    "executedVersion": "$LATEST",
    "functionError": "Unhandled"
  },
  "responsePayload": {
    "errorMessage": "demo failure for case C1-fail",
    "errorType": "RuntimeError",
    "requestId": "<request-id>",
    "stackTrace": [
      "  File \"/var/task/index.py\", line 14, in handler\n    raise RuntimeError(\"demo failure for case \" + str(event.get(\"case\")))\n"
    ]
  }
}

The condition was RetriesExhausted, the approximateInvokeCount was 3, and the format was the same as the example shown on the records page. The responsePayload contained the errorType and stackTrace from the exception thrown by the function.

The body of the message in the dead-letter queue contained only the original input: {"case": "C1-fail"}. The attributes included RequestID, ErrorCode, and ErrorMessage. The RequestID had the same value as the requestId in the destination's record. The ErrorCode was 200, and the ErrorMessage was the exception message. In this article's reading, the ErrorCode of 200 is the same value as the responseContext.statusCode of 200 in the record, because a function error comes back with HTTP status 200 for the request that invoked the function.

The successful event was executed once, and a record was delivered to the on-success destination. The condition was Success, the approximateInvokeCount was 1, and the responsePayload contained the function's return value.

10.3 C2 — Retries Set to 0

A failing event was sent at 08:10:19. The function executed once, and approximately 0.2 seconds after the failure, the message was delivered to both the destination and the dead-letter queue. The destination's record showed a condition of RetriesExhausted and an approximateInvokeCount of 1. The dead-letter queue had the same three attributes as C1.

10.4 C3 — Maximum Event Age Set to 60 Seconds

A failing event was sent at 08:13:46. The function executed only once, and approximately 0.15 seconds after the failure, the message was delivered to both the destination and the dead-letter queue. A second execution did not occur. In the destination's record, the condition was EventAgeExceeded, and the approximateInvokeCount was 1.

Although the retry count remained at 2, Lambda sent the event without waiting one minute. In this article's reading, because the wait before the next attempt exceeds the 60-second maximum event age, Lambda treated the event as expired without waiting. This is an inference, and no statement saying so was found.

10.5 C4 — Reserved Concurrency Set to 0

Reserved concurrency was set to 0, and an event that should have succeeded was sent at 08:20:07. The function did not execute, and after approximately 1-2 seconds, the message was delivered to both the destination and the dead-letter queue.

The destination's record showed a condition of ZeroReservedConcurrency and an approximateInvokeCount of 0. The record did not contain a responseContext or responsePayload.

The dead-letter queue message had two attributes: RequestID and ErrorMessage. It had no ErrorCode. The ErrorMessage was the following string:

Function not invoked. Function isconfigured with zero reserved concurrent executions

Although the input included "succeed": true to indicate a successful outcome, it was sent to the on-failure destination because the function did not execute.

10.6 C5 — Overwriting With a Single Setting

Prior to calling PutFunctionEventInvokeConfig, the configuration had a MaximumRetryAttempts value of 2, a MaximumEventAgeInSeconds value of 21600, and included destination configurations for both successful and failed events. After calling it with only a MaximumRetryAttempts value of 1, the new configuration had a MaximumRetryAttempts value of 1, lacked a MaximumEventAgeInSeconds setting, and the DestinationConfig sections for OnSuccess and OnFailure were empty. As stated in the API reference in Section 5.6, any settings that were not explicitly specified were removed.

10.7 Metrics

The following table presents the aggregated values for time periods C1 through C4, calculated from data points collected every minute.

MetricSum in This SetupNote
AsyncEventsReceived52 in C1, and 1 each in C2, C3, and C4.
Invocations64 in C1 (3 attempts of the failing event and 1 of the succeeding event), 1 each in C2 and C3, and 0 in C4.
Errors5The number of executions of failing events.
AsyncEventsDropped4The failing event in C1, and C2, C3, and C4.
Throttles0No data points were recorded during the C4 time period.
DeadLetterErrors—No data points were recorded.
DestinationDeliveryFailures—No data points were recorded.

The difference of 1 between Invocations and AsyncEventsReceived can be explained by 2 retries in C1 and 1 instance where a function was not executed in C4. This matches this article's reading in Section 8.2. The 4 instances of AsyncEventsDropped all represent events that reached both the destination and the dead-letter queue, consistent with the definition in Section 8.3.

The AsyncEventAge metric had three values in the C1 time period: 42, 64,483, and 173,665. In this article's reading, the unit is milliseconds, and the values show that the second and third attempts of the failing event came about 64 seconds and about 174 seconds after it was queued. The values for C2 and C3 were 39 and 72, respectively.

10.8 What This Setup Cannot Show

This setup sent a small input a few times to one function in one Region. The following cannot be said from this setup:

  • The behavior of Layer A when invoked from S3, SNS, or EventBridge. This setup invoked the function from a local CLI.
  • The intervals of the retries for throttling and system errors, and the 6-hour limit. Apart from the reserved concurrency of 0, this setup caused no throttling.
  • Sending to destinations and dead-letter queues for inputs close to 1 MB. The input in this setup is a few dozen bytes.
  • How events are handled when the function cannot keep up with the queue.
  • Detection of recursive loops.
  • How messages are delivered to destinations and dead-letter queues other than SQS.

11. Failure Modes

This chapter lists failures that can occur when the layers or the callers are confused, drawing on Chapters 3 through 10. Each item represents a possible failure scenario.

  • Assume that failed events will always remain in either a destination or a dead-letter queue, and configure neither. By default, events discarded by Layer B will not be retained (Section 7.1).
  • Configure a dead-letter queue, feel safe, and do not monitor DeadLetterErrors. If sending to the dead-letter queue fails, Lambda deletes the event (Section 7.2).
  • Assume that the reason for failure will be recorded in the dead-letter queue. The dead-letter queue has no details of the response, and only the first 1 KB of ErrorMessage comes as an attribute (Section 6.4).
  • Use a function that receives inputs approaching 1 MB, and configure an SNS topic as either the destination or the dead-letter queue. The records page states that delivery to an SNS destination may fail (Section 7.3).
  • Assume that the function is retried three times, and do not prepare for throttling retries that last up to 6 hours. Three applies only to function errors; throttles and system errors will default to a maximum retry time of up to 6 hours (Sections 5.2 and 5.3).
  • Set MaximumRetryAttempts to 0 and assume that the event will be delivered to the function only once. The throttle retries will continue, and due to the internal queue's behavior, the same event may be delivered multiple times (Sections 5.3 and 5.5).
  • Intend to add a destination, but only specify the destination and call PutFunctionEventInvokeConfig. The retry and maximum event age settings that were not specified are removed (Section 5.6).
  • Assume that EventBridge's default retry behavior is 24 hours without separating buses. The default for new bus subscribers is 5 attempts or 300 seconds; if these limits are reached, the event will be discarded if there is no dead-letter queue (Section 4.6).
  • Assume that EventBridge's MaximumEventAgeInSeconds and Lambda's MaximumEventAgeInSeconds are the same setting. The former applies to Layer A, while the latter applies to Layer B (Section 5.7).
  • Assume that the new bus's dead-letter queue contains events. It receives records of failures (Section 4.6).
  • Read a non-zero AsyncEventsDropped as a sign that events have been lost. The count includes events that were dropped after being sent to a destination or a dead-letter queue (Section 8.3).
  • Configure both a destination and a dead-letter queue, but only monitor one. The current documentation does not confirm whether the event reaches both (Section 6.6).

12. Frequently Asked Questions About Failed Asynchronous Invocations

This chapter answers common questions that readers of Section 1.1 may have, with references to relevant sections in the text.

Q1. Does an event from a failed asynchronous invocation always remain in a destination or a dead-letter queue?

Not always. The event does not remain if there is no destination or dead-letter queue, or if the attempt to send it to the dead-letter queue fails due to permission or size limitations (whether a destination configured alongside the queue still receives a record is No statement found (Section 6.6)). No statement was found on the handling of events that fail to deliver to the destination, or of events that are lost without being sent to a function when the function cannot keep up with the queue (Chapter 7).

Q2. Does Lambda retry a failed asynchronous invocation three times?

Not three. For function errors, the default retry count is two, resulting in a total of three executions, including the initial attempt. For throttling (429) and system errors (500-series), retries are based on time rather than the number of attempts, with a default maximum retry duration of 6 hours (see Section 5.1). The Lambda FAQ words this as "retried at least 3 times" (Section 9.1).

Q3. Does EventBridge retry delivery to a Lambda function up to 185 times within a 24-hour period?

The default for targets on the Custom event bus - Classic is 24 hours and 185 attempts. For subscribers on a new Custom event bus, the default is either 5 attempts or 300 seconds, whichever comes first. Both are Layer A retries before Lambda returns the 202, and after the 202, Lambda's internal queue has its own retries (see Sections 4.5 and 4.6).

Q4. If both a destination and a dead-letter queue are configured, will failed events be delivered to both?

Current documentation does not confirm this. While the API reference indicates that both can be configured, the documentation does not explicitly state whether events will be delivered to both. A blog post from 2019 regarding destinations mentions that errors from function calls are delivered to both (Section 6.6). In the setup in Chapter 10, function errors, passing the maximum event age, and reserved concurrency of 0 all resulted in delivery to both, although this is a single observation (Sections 10.2 through 10.5).

Q5. Does the message from the dead-letter queue indicate the reason for the function's failure?

Only partially. The message received in the dead-letter queue contains the event itself, but not the detailed response from the function. The ErrorCode attribute provides the HTTP status code, and the ErrorMessage attribute contains the first 1 KB of the error message. You can use the RequestID to access the function's logs. If you need the details of the response, use the invocation record sent to the on-failure destination (see Sections 6.2 and 6.4).

Q6. If MaximumRetryAttempts is set to 0, will events only be delivered to the function once?

Not necessarily. MaximumRetryAttempts only applies to retries after the function encounters an error. Retries related to throttling and system errors will continue until the MaximumEventAgeInSeconds is reached. The error handling page notes that, due to the nature of the internal queue, the same event may be delivered multiple times, even if the function does not return an error (see Sections 5.3 and 5.5).

Q7. If the AsyncEventsDropped count increases, should you assume that events have been lost?

Not necessarily. If a destination or dead-letter queue is present, events are sent there and then dropped, and they are counted in AsyncEventsDropped. To determine whether events have actually been lost, consider the DeadLetterErrors, DestinationDeliveryFailures, and the presence of the destination and dead-letter queue (see Section 8.3).

Q8. When S3 event notifications fail to trigger a Lambda function, where do those events go?

The documentation does not provide a definitive answer. The S3 event notification page states that it is designed to deliver notifications at least once, and mentions S3's retry mechanism, but it does not specify the number of retries, the duration, or what happens after those retries are exhausted. The Lambda FAQ says that S3 may retain and retry events rejected by Lambda for 24 hours, without the number of attempts or the destination. After Lambda returns a 202 to the S3 invocation, the same Layer B rules as for the other callers apply (see Sections 4.1 and 4.3).

Q9. If Lambda's MaximumEventAgeInSeconds is set to 21600, will EventBridge also retry events for 6 hours?

No. Lambda's MaximumEventAgeInSeconds is how long the internal queue keeps an event after the 202. EventBridge Classic targets and EventBridge Scheduler have a separate setting with the same name, which determines how long EventBridge retries before the 202. For new bus subscribers, the equivalent setting is MaxEventAgeInSeconds (see Section 5.7).

13. Summary

When a Lambda function is invoked asynchronously and fails, the owner of the event differs before and after Lambda returns a 202 to Invoke. Before the 202, the caller retries, and the caller also decides where events go after its retries run out. SNS sends messages to a subscription's dead-letter queue. The EventBridge Custom event bus - Classic sends events to the target's dead-letter queue, and the new Custom event bus writes a record of the failure to the subscriber's dead-letter queue. If no dead-letter queue is available, the event is discarded. No statement was found on where S3 sends them. After the 202, Lambda's internal queue retries the function. Function errors are retried by default up to two times, while throttling and system errors are retried by default for up to a maximum of six hours. Events whose retries run out are discarded, and the destination receives an invocation record, while the dead-letter queue receives the event payload. If neither is available, nothing is retained. If sending to the dead-letter queue fails, Lambda deletes the event; whether a destination configured alongside the queue still receives a record is No statement found (Section 6.6). No statement was found on what happens to the event when sending to a destination fails.

When reviewing your configuration, verify the following in order:

  • What is the caller? If using EventBridge, is it a Classic bus or a new bus? If using a new bus, what is the InvocationType – EVENT or REQUEST_RESPONSE?
  • Does the caller have a dead-letter queue configured?
  • Does the function have an on-failure destination or a dead-letter queue? If it has both, are you monitoring both?
  • Can the destination and dead-letter queue handle the size of the records and events? For SNS topics, what is the MaximumMessageSize?
  • Where are MaximumRetryAttempts and MaximumEventAgeInSeconds configured – on the function, a version, or an alias? What was the last modification – a Put or an Update?
  • Are there alarms configured for DeadLetterErrors and DestinationDeliveryFailures?
  • Is AsyncEventAge increasing? What is the difference between AsyncEventsReceived and Invocations?

14. References



References:
Tech Blog with curated related content

Written by Hidekazu Konishi