Skip to main content

What is Lambda Handler: Python Function Basics

A Lambda handler is the Python function that AWS Lambda invokes when triggered. The handler receives two arguments—event and context—and must return a value that Lambda serializes to JSON and returns to the caller. Understanding handler mechanics is essential for building robust serverless applications.

What is a Lambda Handler?

A Lambda handler is a Python function designated as the entry point for a Lambda function execution. When you create a function in the AWS Lambda Console or via code, you specify the handler in the format filename.function_name. For example, if your code file is index.py and the function is my_handler, the handler is index.my_handler.

The handler signature is always:

def handler_name(event, context):
# Process event
return response

Lambda automatically calls this function whenever the function is invoked—either through the console, AWS CLI, API Gateway, or any other trigger. The handler is responsible for processing the input (event), performing business logic, and returning a response.

Understanding the Event Object

The event dictionary contains data from the triggering service. Its structure depends on the source. Here are common patterns:

API Gateway HTTP Request:

def lambda_handler(event, context):
# event structure:
# {
# "httpMethod": "GET",
# "path": "/users/123",
# "queryStringParameters": {"format": "json"},
# "headers": {"Content-Type": "application/json"},
# "body": None,
# "isBase64Encoded": false
# }

method = event.get('httpMethod') # 'GET'
path = event.get('path') # '/users/123'
query = event.get('queryStringParameters', {})

return {
'statusCode': 200,
'body': f'Received {method} request to {path}'
}

S3 Bucket Event:

def lambda_handler(event, context):
# event structure:
# {
# "Records": [
# {
# "s3": {
# "bucket": {"name": "my-bucket"},
# "object": {"key": "uploads/photo.jpg", "size": 5000}
# }
# }
# ]
# }

for record in event.get('Records', []):
bucket = record['s3']['bucket']['name']
key = record['s3']['object']['key']
size = record['s3']['object']['size']
print(f'Processed {key} from {bucket} ({size} bytes)')

return {'statusCode': 200, 'message': 'Processing complete'}

The event structure is defined by the triggering service and is always provided as a Python dictionary. AWS documentation specifies the exact schema for each service.

Understanding the Context Object

The context object is a Lambda runtime object that provides metadata about the current invocation. Common properties include:

  • function_name: The name of the Lambda function
  • function_version: The version of the function (e.g., $LATEST)
  • invoked_function_arn: The full ARN of the function
  • memory_limit_in_mb: The memory allocated (as a string, e.g., '128')
  • aws_request_id: Unique request identifier for CloudWatch tracing
  • log_group_name: CloudWatch Logs group for this function
  • log_stream_name: CloudWatch Logs stream for this invocation
  • get_remaining_time_in_millis(): Method returning milliseconds until timeout

Use context for logging, tracing, and detecting timeouts:

import json
import time

def lambda_handler(event, context):
print(f'Function: {context.function_name}')
print(f'Request ID: {context.aws_request_id}')
print(f'Memory: {context.memory_limit_in_mb} MB')

# Simulate work
time.sleep(1)

remaining_ms = context.get_remaining_time_in_millis()
print(f'Time remaining: {remaining_ms} ms')

if remaining_ms < 5000:
raise TimeoutError('Insufficient time to complete')

return {
'statusCode': 200,
'requestId': context.aws_request_id,
'body': json.dumps({'status': 'success'})
}

Handler Return Values and Response Format

The handler's return value is serialized to JSON and returned to the caller. For API Gateway triggers, return a specific structure:

def lambda_handler(event, context):
try:
user_id = event['pathParameters']['id']

# Fetch user from database (example)
user = {'id': user_id, 'name': 'Alice', 'email': '[email protected]'}

return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps(user)
}
except KeyError:
return {
'statusCode': 400,
'body': json.dumps({'error': 'Missing id parameter'})
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)})
}

For non-HTTP triggers (S3, SQS, DynamoDB), return any JSON-serializable value:

def lambda_handler(event, context):
# Process batch of SQS messages
results = []
for record in event['Records']:
message_id = record['messageId']
body = record['body']

# Process and collect result
results.append({'id': message_id, 'processed': True})

return {'statusCode': 200, 'processed_count': len(results)}

Error Handling and Exceptions

When a handler raises an exception, Lambda catches it, formats the error, and returns it to the caller. Log the exception for debugging:

import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
try:
# Validate input
if 'required_field' not in event:
raise ValueError('required_field is missing')

# Process
result = do_work(event['required_field'])

return {
'statusCode': 200,
'body': json.dumps({'result': result})
}

except ValueError as e:
logger.error(f'Validation error: {str(e)}')
return {
'statusCode': 400,
'body': json.dumps({'error': 'Invalid input'})
}

except Exception as e:
logger.exception('Unexpected error')
return {
'statusCode': 500,
'body': json.dumps({'error': 'Internal server error'})
}

def do_work(value):
return f'Processed: {value}'

Structured logging (with correlation IDs from context.aws_request_id) enables tracking requests across logs and related services.

Choosing a Handler Name

The handler name convention is flexible but matters for clarity:

  • lambda_handler — Default name, works for most cases
  • handler — Short, common convention
  • api_handler, s3_handler — Descriptive names for multi-trigger setups

When defining the handler in AWS, use the format filename.function_name. If your code is in app.py and the function is handle_request, the handler value in Lambda Console is app.handle_request.

For a single-file function:

# app.py
def lambda_handler(event, context):
return {'statusCode': 200, 'body': 'OK'}

In the Lambda Console, set Handler to app.lambda_handler.

Passing Custom Data via Event

For testing and custom workflows, pass arbitrary event data:

# aws lambda invoke --function-name my-func --payload '{"user": "bob", "action": "login"}' output.json

def lambda_handler(event, context):
user = event.get('user', 'unknown')
action = event.get('action', 'unknown')

print(f'User {user} performed {action}')

return {'statusCode': 200, 'user': user}

The --payload argument accepts any JSON; AWS automatically decodes it into the event dictionary.

Key Takeaways

  • A Lambda handler is a Python function that receives event and context, processes data, and returns a JSON-serializable response.
  • The event dictionary structure depends on the triggering service; API Gateway, S3, SQS, and DynamoDB each have defined schemas.
  • The context object provides metadata: function name, request ID, remaining time, and CloudWatch references for logging and tracing.
  • Return values are serialized to JSON; for HTTP APIs, use the standard structure with statusCode, headers, and body.
  • Always implement error handling with try-except blocks and log exceptions for production debugging.
  • Handler names are customizable; the format is filename.function_name as specified in Lambda configuration.

Frequently Asked Questions

Can I have multiple handlers in one function?

Yes, but only one is invoked per execution. You can define multiple functions in the same file and change the Handler setting in the Lambda Console to call different ones. For example, app.api_handler vs. app.batch_handler. However, for clarity, a single handler per function is standard.

What happens if my handler doesn't return anything?

If your handler returns None (or no explicit return), Lambda returns null. For API Gateway, this results in a null body, which may be invalid HTTP. Always return a proper response dictionary for API-driven functions.

How do I access environment variables in a handler?

Use import os; os.environ['KEY_NAME']. Define variables in the Lambda Console under Configuration → Environment variables. This pattern is safer than hardcoding secrets; combine with AWS Secrets Manager for sensitive data.

Can I change the handler after deployment?

Yes, in the Lambda Console under Configuration → General, or via aws lambda update-function-configuration --function-name my-func --handler new_file.new_handler. The change applies immediately to new invocations.

What's the maximum size of an event or response?

Event payloads are limited by the triggering service (API Gateway: 10 MB, SQS: 256 KB per message). Response bodies are limited similarly. Always compress large payloads or store them in S3 and pass a reference.

Further Reading