Fix: AWS Lambda Task timed out after X seconds
Part of: Docker, DevOps & Infrastructure
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. The runtime is sent a SIGKILL equivalent and the execution environment may be reused for the next invocation, or it may be replaced if the timeout pattern repeats.
The 15-minute ceiling matters because it constrains how you architect long-running work. Lambda is not the right primitive for a 30-minute report generation. When a timeout fires repeatedly on the same function, you are seeing one of three architectural mismatches: (a) the workload is larger than a single Lambda invocation should handle, (b) the function is blocking on something external that itself has no bound, or (c) the runtime is paying a cold-start cost on every invocation. The fix differs sharply between those three.
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.
Version History That Changes the Failure Mode
Lambda has changed enough since 2014 that what “looks like a timeout problem” depends heavily on which features your account has access to and which runtime you are on. The platform changes below alter the right fix for the same symptom.
- Oct 2018 — maximum timeout raised from 5 minutes to 15 minutes. Before this, anything longer than 5 minutes required Step Functions or a different compute service. If you read older articles claiming “Lambda can only run 5 minutes,” they predate this change. The 15-minute ceiling has been stable ever since.
- Dec 2019 — Provisioned Concurrency GA. This is the supported way to eliminate cold starts for production workloads. Before this, the only mitigations were CloudWatch warmer pings, which AWS now explicitly recommends against in favour of Provisioned Concurrency.
- Dec 2020 — container image support (up to 10 GB). Functions packaged as OCI images cold-start differently than ZIP packages. Large images can take significantly longer to initialize on the first invocation, so “my function times out only on cold start” is more common on container-image Lambdas.
- Sept 2021 — arm64 (Graviton2) GA. Switching the architecture from x86_64 to arm64 typically lowers both cost and execution time for compute-bound code, which directly helps timeout-sensitive workloads. The trade-off is occasional native-dependency rebuilds.
- Nov 2022 — SnapStart for Java. SnapStart drastically reduces cold-start time for JVM-based functions by snapshotting the initialized runtime. Java timeouts that were really cold-start timeouts often disappear after enabling SnapStart. Support has since extended beyond the original Java runtimes.
- 2023 onward — runtime deprecations. Node.js 14 reached EOL in late 2023; Node.js 16 followed; Python 3.8 was deprecated in 2024. AWS eventually blocks function updates and then invocations on deprecated runtimes. A “function suddenly times out” can in some cases be the platform’s deprecation behaviour, not your code.
- 2024 —
INIT_REPORTand improved cold-start visibility. Newer log lines surface the actual initialization duration separately from invocation duration, so you can confirm whether a 5-second total is “4.8s cold start + 0.2s work” or “0.1s cold start + 4.9s work.” Older articles often conflate the two.
If your function is on a Lambda runtime older than the current LTS, upgrading is often the single biggest performance improvement you can make before tuning anything else.
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: PublishedVersionsFor dedicated treatment of cold-start tuning, see Fix: AWS Lambda cold start timeout.
Fix 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.
Check the runtime version is still supported. AWS deprecates older Node.js, Python, and Java runtimes on a published schedule. Once a runtime is deprecated and then blocked, invocations can hang or fail in ways that look like timeouts in CloudWatch. Run aws lambda get-function-configuration --function-name <name> and confirm the Runtime is on the current supported list. Upgrading to a current runtime often eliminates a class of mysterious slowdowns.
Check for ENI exhaustion in VPC. When Lambda concurrency spikes in a VPC, it creates Hyperplane ENIs. The subnet running out of free IP addresses causes new invocations to wait for ENI allocation, which surfaces as a timeout. Either increase the subnet CIDR range or spread the function across more subnets. For the related RDS-side symptom, see Fix: AWS RDS connection timed out.
Check for SnapStart not being applied to the version you invoke. SnapStart applies only to published versions, not $LATEST. If your alias still points to $LATEST, you get no benefit. Confirm the alias targets a published version where SnapStart is Active. See Fix: AWS Lambda SnapStart not working.
For credential configuration issues that might prevent Lambda from finishing in time, see the related AWS error guides on the site.
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 Lambda Cold Start Timeout and Slow First Invocation
How to fix AWS Lambda cold start timeouts and slow first invocations — provisioned concurrency, reducing package size, connection reuse, and language-specific optimizations.
Fix: AWS Lambda SnapStart Not Working — Version vs Alias, Restore Hooks, and Uniqueness Bugs
How to fix Lambda SnapStart errors — feature requires published version, $LATEST not supported, restore hook for stale connections, UUID collisions after snapshot, time-based state staleness, and pricing surprises.
Fix: AWS Step Functions Not Working — ASL Syntax, Map State, Error Handling, and IAM
How to fix AWS Step Functions errors — Amazon States Language syntax, Standard vs Express workflows, Distributed Map for large datasets, Retry/Catch error handling, Lambda invoke optimization, and IAM execution role permissions.
Fix: SST Not Working — Deploy Failing, Bindings Not Linking, or Lambda Functions Timing Out
How to fix SST (Serverless Stack) issues — resource configuration with sst.config.ts, linking resources to functions, local dev with sst dev, database and storage setup, and deployment troubleshooting.