Skip to content

Fix: AWS Lambda Task timed out after X seconds

FixDevs ·

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 seconds

Or in CloudWatch logs:

2024-01-15T10:30:00.000Z abc123 Task timed out after 15.00 seconds
REPORT RequestId: abc123 Duration: 15003.45 ms Billed Duration: 15000 ms Memory Size: 128 MB Max Memory Used: 95 MB Status: timeout

The 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:

  1. Go to Lambda → Your function → Configuration → General configuration
  2. Click Edit
  3. Set Timeout to an appropriate value (e.g., 30 seconds, 5 minutes)
  4. Save

AWS CLI:

aws lambda update-function-configuration \
  --function-name my-function \
  --timeout 60

Terraform:

resource "aws_lambda_function" "my_function" {
  function_name = "my-function"
  timeout       = 60  # seconds
  # ...
}

SAM template:

MyFunction:
  Type: AWS::Serverless::Function
  Properties:
    Timeout: 60

CDK (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., urllib3 instead of requests)

Use Provisioned Concurrency:

aws lambda put-provisioned-concurrency-config \
  --function-name my-function \
  --qualifier my-alias \
  --provisioned-concurrent-executions 5

This 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: PublishedVersions

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:

  1. Create a public subnet with an Internet Gateway
  2. Create a NAT Gateway in the public subnet
  3. 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-456

VPC 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
MemoryCPUCost per 100ms
128 MB~0.08 vCPU$0.000000208
512 MB~0.3 vCPU$0.000000833
1024 MB~0.6 vCPU$0.000001667
1769 MB1 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.yaml

This 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 10

Add 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: Active

X-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.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles