Lambda Destinations let you route your function’s output to other AWS services, making it a powerful tool for building event-driven architectures.
Here’s a Lambda function that processes SQS messages. When it successfully processes a message, it sends a confirmation to an SNS topic. If it fails, it sends the error details to another SNS topic.
import json
import boto3
sns_client = boto3.client('sns')
SUCCESS_TOPIC_ARN = 'arn:aws:sns:us-east-1:123456789012:my-lambda-success-topic'
FAILURE_TOPIC_ARN = 'arn:aws:sns:us-east-1:123456789012:my-lambda-failure-topic'
def lambda_handler(event, context):
for record in event['Records']:
try:
message_body = record['body']
print(f"Processing message: {message_body}")
# Simulate processing logic
if "error" in message_body.lower():
raise ValueError("Simulated processing error")
# If successful, publish to success topic
sns_client.publish(
TopicArn=SUCCESS_TOPIC_ARN,
Message=json.dumps({
'message_id': record['messageId'],
'body': message_body,
'status': 'SUCCESS'
})
)
print(f"Successfully processed message ID: {record['messageId']}")
except Exception as e:
# If failed, publish to failure topic
sns_client.publish(
TopicArn=FAILURE_TOPIC_ARN,
Message=json.dumps({
'message_id': record.get('messageId'),
'body': message_body,
'error': str(e),
'status': 'FAILURE'
})
)
print(f"Failed to process message ID: {record.get('messageId')}. Error: {e}")
# Depending on your SQS configuration, you might want to re-raise
# or handle the error to control visibility timeout.
# For this example, we'll let Lambda Destinations handle the routing.
return {
'statusCode': 200,
'body': json.dumps('Processed records.')
}
When you configure Lambda Destinations, you specify the target (like an SNS topic, SQS queue, or another Lambda function) for either successful or failed invocations. The Lambda service automatically sends the event payload, including the input event, the function’s response, and execution details, to the configured destination.
Let’s break down how this works internally. When your Lambda function executes, its output is captured. If the execution completes without an unhandled exception, the output is considered a success. If an unhandled exception occurs, it’s a failure. Lambda then inspects the invocation result. For successes, it includes the response field from your handler. For failures, it includes errorResult which contains the error type and message. This structured output is what gets sent to your configured Destinations.
Here’s a typical configuration in the AWS console. You navigate to your Lambda function, scroll down to "Asynchronous invocation," and under "Destinations," you’ll see options for "On success" and "On failure." You select the type of destination (e.g., SNS) and then choose the specific resource (e.g., your SNS topic ARN).
The event payload sent to the destination is a JSON object containing the invocationResponse (for success) or invocationError (for failure), along with the original event and context objects. For asynchronous invocations like SQS triggers, the payload sent to the destination is slightly different. It includes the requestPayload (the event that triggered the function), the responsePayload (the function’s return value or error details), and metadata about the invocation.
Consider the invocationResponse for a successful SQS processing: it will contain the list of processed message IDs or a summary. For a failure, invocationError will have the error type, message, and stack trace. This detailed context is invaluable for debugging and for triggering downstream processes based on specific outcomes.
When you configure a destination for an SQS-triggered Lambda, the event payload sent is structured like this:
{
"version": "1.0",
"invocationId": "...",
"functionName": "my-lambda-function",
"functionArn": "arn:aws:lambda:us-east-1:123456789012:function:my-lambda-function",
"invocationRequest": {
"event": { // The original SQS event
"Records": [
{
"messageId": "...",
"body": "...",
// ... other SQS message attributes
}
]
},
"context": { // The Lambda context object
"awsRequestId": "...",
"invokedFunctionArn": "...",
// ... other context properties
}
},
"invocationResponse": { // For success
"status": 200,
"body": { // The return value of your lambda_handler
"statusCode": 200,
"body": "Processed records."
}
},
"invocationError": { // For failure
"errorType": "ValueError",
"errorMessage": "Simulated processing error",
"stackTrace": [...]
}
}
It’s crucial to understand that Lambda Destinations are for asynchronous invocations. If your Lambda function is triggered synchronously (e.g., via API Gateway or direct invocation), the return value of your handler is directly returned to the caller, and Destinations are not used for the function’s primary output. Destinations are specifically designed to handle the eventual outcome of asynchronous events.
A common point of confusion is how the response from your lambda_handler function relates to the invocationResponse or invocationError payload sent to the destination. For SQS, if your handler returns successfully (no unhandled exceptions), the entire return dictionary is placed within the invocationResponse.body. If an unhandled exception occurs, the error details are populated into invocationError. The key is that the destination receives a structured object containing the result of the invocation, not just the raw return value of your handler.
The next step after routing success and failure events is to implement robust error handling and retry strategies for your downstream services.