Fix: AWS Lambda Task timed out after X seconds
Quick Answer
How to fix AWS Lambda timeout errors caused by low timeout settings, cold starts, slow external API calls, VPC configuration, and unoptimized code.
The Error
Your Lambda function fails with:
Task timed out after 3.00 secondsOr in CloudWatch logs:
2024-01-15T10:30:00.000Z abc123 Task timed out after 15.00 secondsREPORT RequestId: abc123 Duration: 15003.45 ms Billed Duration: 15000 ms Memory Size: 128 MB Max Memory Used: 95 MB Status: timeoutThe Lambda function did not complete within its configured timeout. AWS forcefully terminated the execution.
Why This Happens
Every Lambda function has a timeout setting (default 3 seconds, maximum 15 minutes). If the function does not return before the timeout, AWS kills the execution. There is no graceful shutdown — the process is terminated immediately.
Common causes:
- Timeout too low. The default 3-second timeout is often insufficient for real workloads.
- Slow external API calls. The function calls a third-party API, database, or another AWS service that responds slowly.
- Cold starts. The first invocation after a period of inactivity takes longer because AWS must initialize the runtime.
- VPC NAT Gateway delays. Lambda functions in a VPC need a NAT Gateway for internet access, which adds latency.
- Unoptimized code. Inefficient algorithms, large file processing, or unnecessary work in the handler.
- Connection pooling issues. Creating new database connections on every invocation instead of reusing them.
- Infinite loops or deadlocks. A bug in the code causes it to run indefinitely.
Fix 1: Increase the Timeout
The simplest fix. Increase the timeout to accommodate your function’s actual execution time:
AWS Console:
- Go to Lambda → Your function → Configuration → General configuration
- Click Edit
- Set Timeout to an appropriate value (e.g., 30 seconds, 5 minutes)
- Save
AWS CLI:
aws lambda update-function-configuration \
--function-name my-function \
--timeout 60Terraform:
resource "aws_lambda_function" "my_function" {
function_name = "my-function"
timeout = 60 # seconds
# ...
}SAM template:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Timeout: 60CDK (TypeScript):
new lambda.Function(this, "MyFunction", {
timeout: cdk.Duration.seconds(60),
});Pro Tip: Set the timeout to 2-3x the expected maximum execution time. If your function typically takes 5 seconds and occasionally takes 15 seconds, set the timeout to 30-45 seconds. This provides headroom without leaving runaway functions running indefinitely. Lambda billing is per-millisecond, so a higher timeout does not cost more unless the function actually runs longer.
Fix 2: Optimize Cold Starts
Cold starts add initialization time to the first invocation. Heavy runtimes (Java, .NET) can add several seconds.
Move initialization outside the handler:
# WRONG — initializes on every invocation
def handler(event, context):
import boto3
client = boto3.client("dynamodb")
return client.get_item(...)
# RIGHT — initializes once, reused across invocations
import boto3
client = boto3.client("dynamodb")
def handler(event, context):
return client.get_item(...)Module-level code runs once during cold start and is reused for subsequent warm invocations.
Reduce package size:
- Remove unused dependencies
- Use Lambda layers for shared code
- Use lightweight alternatives (e.g.,
urllib3instead ofrequests)
Use Provisioned Concurrency:
aws lambda put-provisioned-concurrency-config \
--function-name my-function \
--qualifier my-alias \
--provisioned-concurrent-executions 5This keeps 5 execution environments warm at all times, eliminating cold starts. But it costs money even when not processing requests.
For Java: Use GraalVM native image or SnapStart:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: java21
SnapStart:
ApplyOn: PublishedVersionsFix 3: Optimize External API Calls
External API calls are the most common cause of Lambda timeouts:
Set timeouts on HTTP clients:
Python (boto3):
import boto3
from botocore.config import Config
config = Config(
connect_timeout=5,
read_timeout=10,
retries={"max_attempts": 2}
)
client = boto3.client("dynamodb", config=config)Node.js:
const response = await fetch(url, {
signal: AbortSignal.timeout(5000), // 5 second timeout
});Check remaining time before making calls:
def handler(event, context):
remaining_ms = context.get_remaining_time_in_millis()
if remaining_ms < 5000:
return {"statusCode": 408, "body": "Not enough time"}
result = slow_api_call()
return {"statusCode": 200, "body": result}Common Mistake: Not setting timeouts on HTTP clients inside Lambda. Without a client-side timeout, the Lambda function waits until its own timeout is hit, giving you no information about which call was slow. Always set HTTP client timeouts shorter than the Lambda timeout.
Fix 4: Fix VPC and Network Configuration
Lambda functions in a VPC cannot access the internet by default. Without a NAT Gateway, any external API call (including AWS service APIs) hangs until timeout.
Symptoms:
- Function works outside VPC but times out in VPC
- All AWS SDK calls time out
- External API calls time out
Fix: Add a NAT Gateway:
- Create a public subnet with an Internet Gateway
- Create a NAT Gateway in the public subnet
- Route the Lambda function’s private subnet through the NAT Gateway
Fix: Use VPC Endpoints for AWS services:
Instead of routing through NAT, create VPC endpoints for AWS services:
aws ec2 create-vpc-endpoint \
--vpc-id vpc-123 \
--service-name com.amazonaws.us-east-1.dynamodb \
--route-table-ids rtb-456VPC endpoints let Lambda access DynamoDB, S3, SQS, etc. without leaving the VPC, eliminating the need for a NAT Gateway for AWS service calls.
For general IAM permission issues with Lambda, see Fix: AWS AccessDeniedException.
Fix 5: Optimize Database Connections
Creating a new database connection on every invocation is slow:
Broken — new connection per invocation:
def handler(event, context):
conn = psycopg2.connect("postgresql://...") # Slow!
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
conn.close()
return cursor.fetchall()Fixed — reuse connections across invocations:
import psycopg2
conn = None
def get_connection():
global conn
if conn is None or conn.closed:
conn = psycopg2.connect("postgresql://...")
return conn
def handler(event, context):
conn = get_connection()
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
return cursor.fetchall()Better — use RDS Proxy:
RDS Proxy manages connection pooling for you:
import boto3
rds_client = boto3.client("rds")
token = rds_client.generate_db_auth_token(
DBHostname="my-proxy.proxy-abc123.us-east-1.rds.amazonaws.com",
Port=5432,
DBUsername="lambda_user"
)Fix 6: Increase Memory (Also Increases CPU)
Lambda allocates CPU proportional to memory. More memory = faster execution:
aws lambda update-function-configuration \
--function-name my-function \
--memory-size 512| Memory | CPU | Cost per 100ms |
|---|---|---|
| 128 MB | ~0.08 vCPU | $0.000000208 |
| 512 MB | ~0.3 vCPU | $0.000000833 |
| 1024 MB | ~0.6 vCPU | $0.000001667 |
| 1769 MB | 1 vCPU | $0.000002875 |
A function that takes 10 seconds at 128 MB might take 2 seconds at 512 MB — and cost less because you are billed per millisecond.
Use AWS Lambda Power Tuning to find the optimal memory:
# Install the power tuning tool
sam deploy --template-file powertuning.yamlThis runs your function at different memory sizes and shows the cost/performance tradeoff.
Fix 7: Use Async Patterns for Long Tasks
If the task genuinely takes longer than 15 minutes (Lambda’s maximum timeout), break it up:
Step Functions:
Orchestrate multiple Lambda functions as a state machine:
{
"StartAt": "ProcessData",
"States": {
"ProcessData": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:process",
"Next": "GenerateReport"
},
"GenerateReport": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:report",
"End": true
}
}
}SQS for background processing:
Return immediately and process asynchronously:
import boto3
sqs = boto3.client("sqs")
def handler(event, context):
# Queue the work instead of doing it inline
sqs.send_message(
QueueUrl="https://sqs.us-east-1.amazonaws.com/123/my-queue",
MessageBody=json.dumps(event)
)
return {"statusCode": 202, "body": "Accepted"}Fix 8: Debug Timeout Issues
Check CloudWatch Logs:
aws logs filter-log-events \
--log-group-name /aws/lambda/my-function \
--filter-pattern "Task timed out" \
--limit 10Add timing to your function:
import time
def handler(event, context):
start = time.time()
result1 = step1()
print(f"Step 1: {time.time() - start:.2f}s")
result2 = step2()
print(f"Step 2: {time.time() - start:.2f}s")
return {"statusCode": 200}Enable X-Ray tracing:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Tracing: ActiveX-Ray shows a visual breakdown of where time is spent — SDK calls, external APIs, database queries.
Still Not Working?
Check for recursive invocations. A Lambda function that triggers itself (through S3, SNS, or DynamoDB Streams) can create an infinite loop. Add a deduplication check or limit recursion depth.
Check for DNS resolution delays. In a VPC, DNS resolution might be slow. Use VPC DNS settings or cache DNS lookups.
Check for large payloads. Processing large S3 objects or API payloads takes time. Stream data instead of loading it all into memory.
Check for SDK retries. AWS SDK clients retry failed requests with exponential backoff by default. Three retries with backoff can add 30+ seconds. Configure retry limits:
config = Config(retries={"max_attempts": 1})Check for frozen execution environments. Lambda freezes the execution environment between invocations. If your code relies on background threads or timers, they are paused during the freeze and resume on the next invocation, which can cause unexpected behavior.
For credential configuration issues that might prevent Lambda from accessing other AWS services, see Fix: AWS unable to locate credentials. For Terraform issues when deploying Lambda, see Fix: Terraform error acquiring state lock.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: AWS CloudFormation stack in ROLLBACK_COMPLETE or CREATE_FAILED state
How to fix AWS CloudFormation ROLLBACK_COMPLETE and CREATE_FAILED errors caused by IAM permissions, resource limits, invalid parameters, and dependency failures.
Fix: AWS AccessDeniedException when calling an AWS service operation
How to fix AWS AccessDeniedException caused by missing IAM permissions, explicit denies, SCPs, resource policies, permission boundaries, and misconfigured roles.
Fix: Terraform Error locking state: Error acquiring the state lock
How to fix Terraform state lock error caused by concurrent runs, crashed operations, DynamoDB lock table issues, and stale lock IDs.
Fix: AWS S3 Access Denied (403 Forbidden) when uploading, downloading, or listing
How to fix the 'Access Denied' (403 Forbidden) error in AWS S3 when uploading, downloading, listing, or managing objects using the CLI, SDK, or console.