How to Fix AWS Lambda Timeout Error: A Complete Troubleshooting Guide
If you’ve deployed a Lambda function and watched it die with the dreaded Task timed out after X seconds message, you’re in good company. Lambda timeouts are one of the most common — and frustrating — issues developers face when building serverless applications on AWS.
I’ve spent years debugging Lambda functions in production, and I can tell you that timeout errors rarely have a single obvious cause. Sometimes it’s a misconfigured VPC. Sometimes it’s a database connection pool that’s exhausting itself. And sometimes it’s just that your function legitimately needs more time than the default three seconds.
This guide walks you through exactly how to fix AWS Lambda timeout errors, starting with the most common causes and working down to edge cases that’ll have you pulling your hair out if you don’t know where to look.
Understanding the AWS Lambda Timeout Error
Before diving into fixes, let’s make sure we understand what’s actually happening.
What the Error Looks Like
When a Lambda function times out, you’ll see something like this in your CloudWatch Logs:
START RequestId: 8f3a2b1c-4d5e-6f7g-8h9i-0j1k2l3m4n5o Version: $LATEST
END RequestId: 8f3a2b1c-4d5e-6f7g-8h9i-0j1k2l3m4n5o
REPORT RequestId: 8f3a2b1c-4d5e-6f7g-8h9i-0j1k2l3m4n5o Duration: 3000.45 ms Billed Duration: 3001 ms Memory Size: 128 MB Max Memory Used: 78 MB
INIT_START RequestId: 8f3a2b1c-4d5e-6f7g-8h9i-0j1k2l3m4n5o Version: $LATEST
2026-01-15T10:23:45.123Z 8f3a2b1c-4d5e-6f7g-8h9i-0j1k2l3m4n5o Task timed out after 3.00 seconds
The key line is that final one: Task timed out after 3.00 seconds. That’s Lambda’s way of saying your function exceeded its configured timeout limit.
Lambda Timeout Limits (2026)
As of 2026, here are the hard limits you need to know:
| Parameter | Minimum | Maximum |
|---|---|---|
| Function timeout | 1 second | 15 minutes (900 seconds) |
| Memory allocation | 128 MB | 10,240 MB (10 GB) |
/tmp storage |
512 MB | 10,240 MB |
The default timeout for a new Lambda function is 3 seconds. That’s the source of many timeout issues right there — developers deploy a function that processes data or makes API calls, and it hits that default wall almost immediately.
Root Cause Analysis: Why Lambda Functions Time Out
Let me walk you through the most common root causes, roughly ordered by frequency based on what I’ve seen in production environments.
Cause 1: The Default Timeout Is Too Low
This is the most common cause by far. You create a function, it makes a few API calls or processes some data, and suddenly you’re hitting that 3-second default. The fix is trivial, but understanding why it happened matters.
Cause 2: Cold Start Latency
When Lambda provisions a new execution environment, there’s a delay called a cold start. If your function has heavy dependencies (looking at you, AWS SDK v2 and large Node.js modules), the initialization alone can eat into your timeout budget.
Cause 3: VPC Misconfiguration
If your function is attached to a VPC and the VPC isn’t configured correctly, network calls will hang until timeout. This is particularly insidious because the error message doesn’t tell you anything about networking — it just says “timed out.”
Cause 4: Inefficient Code or Database Queries
A N+1 query problem, a missing database index, or a synchronous loop processing thousands of items can easily push you past any timeout limit.
Cause 5: External API Bottlenecks
Third-party APIs that are slow or have rate limits can cause your function to wait indefinitely if you haven’t set proper client-side timeouts.
Cause 6: Memory-Linked CPU Throttling
This one surprises people: Lambda allocates CPU proportionally to memory. A function with 128 MB of RAM gets significantly less CPU than one with 1,792 MB (which is the point where you get a full vCPU). If your function is CPU-intensive, low memory means slow execution.
Step-by-Step Solutions
Now let’s go through the fixes, from the simplest to the most complex.
Solution 1: Increase the Timeout (Quick Fix)
If your function legitimately needs more time — say it’s processing a file or making sequential API calls — just increase the timeout. You can do this in the AWS Console, via CLI, or in your infrastructure code.
Via AWS CLI:
aws lambda update-function-configuration \
--function-name my-function \
--timeout 30
Via Terraform:
resource "aws_lambda_function" "my_function" {
function_name = "my-function"
handler = "index.handler"
runtime = "python3.12"
timeout = 30
memory_size = 256
filename = "deployment-package.zip"
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
role = aws_iam_role.lambda_role.arn
}
Via AWS SDK (Python with Boto3):
import boto3
lambda_client = boto3.client('lambda')
response = lambda_client.update_function_configuration(
FunctionName='my-function',
Timeout=30,
MemorySize=512
)
print(f"Updated function configuration: {response['LastModified']}")
One important note: don’t just crank the timeout to 15 minutes and call it a day. Longer timeouts mean higher costs if your function is genuinely stuck, and they can mask underlying problems. Set the timeout to a reasonable value based on your function’s expected execution time, plus a buffer.
A good rule of thumb: set your timeout to 1.5x the p99 execution time you observe in CloudWatch metrics.
Solution 2: Optimize Cold Starts
If your function times out intermittently — especially on the first invocation after deployment or during traffic spikes — cold starts are likely the culprit.
Use Provisioned Concurrency
Provisioned concurrency keeps execution environments warm and ready to respond immediately:
aws lambda put-provisioned-concurrency \
--function-name my-function \
--qualifier production \
--provisioned-concurrent-executions 10
Initialize Outside the Handler
This is a simple but powerful pattern. Move database connections, SDK clients, and configuration loads outside your handler function so they only execute during cold starts, not on every invocation:
import json
import boto3
import psycopg2
from psycopg2.pool import SimpleConnectionPool
# These execute ONLY during cold start
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('my-table')
# Connection pool is created once and reused
db_pool = SimpleConnectionPool(
minconn=1,
maxconn=5,
host='my-db.example.com',
database='mydb',
user='myuser',
password='mypass'
)
def lambda_handler(event, context):
# This code runs on EVERY invocation
try:
# Reuse the existing connection
conn = db_pool.getconn()
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = %s", (event['user_id'],))
result = cursor.fetchone()
return {
'statusCode': 200,
'body': json.dumps({'user': result})
}
finally:
db_pool.putconn(conn)
Choose a Lightweight Runtime
Runtime choice significantly impacts cold start times. Based on my experience and community benchmarks in 2026:
- Fastest: Rust, Go, C++ — near-instant cold starts (typically under 100ms)
- Fast: Python, Node.js with minimal dependencies — 200-500ms
- Moderate: Java, .NET — can be 1-3 seconds with large frameworks like Spring Boot
If cold starts are killing you and you’re on Java with Spring Boot, consider migrating to Quarkus or GraalVM native images, which dramatically reduce startup time.
Solution 3: Fix VPC-Related Timeouts
This is the edge case that trips up a lot of developers. When you attach a Lambda function to a VPC (usually to access a database in a private subnet), you need specific networking configuration. Get it wrong, and every network call hangs until timeout.
Here’s a checklist to verify your VPC setup:
Verify NAT Gateway Configuration
If your Lambda function needs internet access (for third-party APIs, AWS services not backed by VPC endpoints, etc.) AND is in a private subnet, you must have a NAT Gateway:
# Route table for private subnet with Lambda
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main.id
}
tags = {
Name = "lambda-private-rt"
}
}
resource "aws_route_table_association" "private" {
subnet_id = aws_subnet.private_a.id
route_table_id = aws_route_table.private.id
}
Check Security Group Rules
Your Lambda function’s security group needs outbound rules allowing traffic to your database’s port:
resource "aws_security_group" "lambda_sg" {
name = "lambda-sg"
description = "Security group for Lambda function"
vpc_id = aws_vpc.main.id
egress {
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = [aws_subnet.private_a.cidr_block, aws_subnet.private_b.cidr_block]
}
egress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
Consider VPC Endpoints Instead
If your function only needs to access AWS services like DynamoDB, S3, or SQS, VPC endpoints are often better than NAT Gateways (cheaper and faster):
resource "aws_vpc_endpoint" "dynamodb" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.${var.region}.dynamodb"
vpc_endpoint_type = "Gateway"
route_table_ids = [aws_route_table.private.id]
}
I once spent two days debugging a Lambda timeout that turned out to be a missing VPC endpoint for DynamoDB. The function was in a VPC, DynamoDB isn’t VPC-backed by default, and without a NAT gateway or VPC endpoint, every DynamoDB call just hung until timeout. Don’t be like me — check your network path first.
Solution 4: Optimize Database Connections
Database connection issues are a frequent timeout cause, especially in functions that connect to RDS or Aurora databases.
Use Amazon RDS Proxy
RDS Proxy manages a connection pool that your Lambda functions share, eliminating the overhead of establishing new TCP connections on every invocation:
import os
import psycopg2
def lambda_handler(event, context):
# Connect through RDS Proxy endpoint
conn = psycopg2.connect(
host=os.environ['DB_PROXY_ENDPOINT'], # e.g., proxy-abc123.proxy-abcd1234.us-east-1.rds.amazonaws.com
port=5432,
dbname='mydb',
user=os.environ['DB_USER'],
password=os.environ['DB_PASSWORD'],
sslmode='require',
connect_timeout=3 # Don't wait forever
)
try:
with conn.cursor() as cursor:
cursor.execute("SELECT * FROM orders WHERE user_id = %s", (event['user_id'],))
rows = cursor.fetchall()
return {'statusCode': 200, 'body': str(rows)}
finally:
conn.close()
Always Set Client-Side Timeouts
This is critical. Never let a database client or HTTP client wait indefinitely. Always set a timeout that’s less than your Lambda timeout:
import boto3
from botocore.config import Config
# Set client timeout shorter than Lambda timeout
client_config = Config(
connect_timeout=2,
read_timeout=5,
retries={'max_attempts': 2}
)
s3 = boto3.client('s3', config=client_config)
def lambda_handler(event, context):
try:
response = s3.get_object(
Bucket='my-bucket',
Key=event['file_key']
)
return {'statusCode': 200, 'body': response['Body'].read().decode()}
except Exception as e:
return {'statusCode': 500, 'body': f'Error: {str(e)}'}
For Node.js, the same principle applies using AbortController:
export const handler = async (event) => {
const controller = new AbortController();
// Set a timeout that's shorter than Lambda's timeout
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch('https://api.example.com/data', {
signal: controller.signal,
headers: { 'Content-Type': 'application/json' }
});
const data = await response.json();
return { statusCode: 200, body: JSON.stringify(data) };
} catch (error) {
if (error.name === 'AbortError') {
return { statusCode: 504, body: 'External API timed out' };
}
return { statusCode: 500, body: error.message };
} finally {
clearTimeout(timeout);
}
};
Solution 5: Fix Memory and CPU Allocation
Since Lambda ties CPU power to memory allocation, a memory-starved function that’s doing computation-heavy work will run slowly and potentially time out.
Here’s a quick reference for CPU allocation as of 2026:
| Memory | vCPUs (approximate) |
|---|---|
| 128 MB | 0.083 |
| 512 MB | 0.333 |
| 1,769 MB | ~1.0 |
| 3,538 MB | ~2.0 |
| 5,307 MB | ~3.0 |
| 10,240 MB | ~6.0 |
If your function is doing JSON parsing, image processing, data transformation, or any CPU-intensive work, bumping memory often reduces execution time enough to solve the timeout:
aws lambda update-function-configuration \
--function-name my-function \
--memory-size 2048 \
--timeout 10
I recommend using AWS’s built-in AWS Lambda Power Tuning tool to find the optimal memory configuration. It runs your function at different memory settings and shows you the cost vs. performance tradeoff.
Solution 6: Handle Retry Storms and Idempotency
When a Lambda function times out, AWS event sources may automatically retry. This can create a cascade of retries that makes the timeout problem worse.
For SQS-Triggered Functions
Configure the visibility timeout to be at least 6x your Lambda timeout:
resource "aws_lambda_event_source_mapping" "sqs_trigger" {
event_source_arn = aws_sqs_queue.my_queue.arn
function_name = aws_lambda_function.my_function.arn
batch_size = 10
maximum_batching_window_in_seconds = 0
# Important: visibility timeout should be 6x Lambda timeout
# This prevents the message from becoming visible again while Lambda is still processing
}
resource "aws_sqs_queue" "my_queue" {
name = "my-queue"
visibility_timeout_seconds = 180 # 6x a 30-second Lambda timeout
receive_wait_time_seconds = 20
}
Implement Idempotency
Make your function idempotent so retries don’t cause duplicate side effects:
“`python
import json
import boto3
from botocore.exceptions import ClientError
dynamodb = boto3.resource(‘dynamodb’)
processed_table = dynamodb.Table(‘processed-events’)
def lambda_handler(event, context):
message_id = event[‘Records’][0][‘messageId’]
# Check if we've already processed this message
try:
response = processed_table.get_item(Key={'messageId': message_id})
if 'Item' in response:
print(f"Message {message_id} already processed, skipping")
return {'statusCode': 200, 'body': 'Already processed'}