Quick answer: Raise the function's timeout in the console or with aws lambda update-function-configuration --timeout, then either make the code faster or move the long work to SQS, Step Functions, or Fargate.
That Task timed out after 3.00 seconds message in CloudWatch almost never shows up on a quiet Tuesday. It shows when someone uploads a 40MB CSV, when a downstream Stripe API hiccups, or when a new deploy adds an N+1 query nobody noticed in staging. Lambda kills your function at the configured limit and writes the timeout to the log stream. The work stops mid-flight. Retries may or may not fire depending on the invocation type.
Lambda caps every invocation at 15 minutes. That ceiling is hard. If you're hitting it, you've got a design problem, not a config problem.
Step 1: Confirm the timeout is really the cause
- Open the CloudWatch log group for your function, usually
/aws/lambda/your-function-name. - Search for
Task timed out. You'll see something likeTask timed out after 30.00 seconds. - Check the
REPORTline right after. It showsDuration,Billed Duration, andMemory Size. IfDurationmatches your timeout setting, you've confirmed it. - Scroll up. The last log line before the timeout tells you where execution stalled. A stuck HTTP call, a slow query, a huge file read — that's your culprit.
Step 2: Raise the timeout (the quick fix)
In the AWS console, open the function, go to Configuration, General configuration, Edit, and change the timeout. Save. That's it.
From the CLI:
aws lambda update-function-configuration \
--function-name my-function \
--timeout 120
After running it, you should see a JSON response with "Timeout": 120. Wait about 30 seconds before invoking again — config changes propagate.
How high should you go? Set it to roughly 2x your p99 duration, not 2x your worst-case outlier. Setting it to 900 "just in case" means a genuinely stuck function burns 15 minutes of your account time on every retry. The default 3 seconds is way too tight for anything doing network I/O. 30 to 60 seconds covers most API-ish functions.
Step 3: Bump memory to get more CPU
Lambda ties CPU to memory. At 128MB you get a fraction of a vCPU. At 1,769MB you get one full vCPU, and it scales linearly from there. If your timeout is 10 seconds and you're parsing JSON, doubling memory often halves runtime.
aws lambda update-function-configuration \
--function-name my-function \
--memory-size 1024
After this runs, invoke once and check the REPORT line. Compare Duration before and after. If it dropped a lot, you were CPU-starved, not timeout-starved. The AWS Lambda Power Tuning tool (a Step Functions state machine you deploy) sweeps memory sizes and tells you the cheapest setting that meets your latency target. Run it before guessing.
Step 4: Find the actual slow call
Raise the timeout and the function passes — great, but it's still slow. Now profile it. Turn on AWS X-Ray tracing (Configuration, Monitoring and operations tools, Active tracing), add the SDK to your handler, and redeploy. The trace map shows each downstream call with its own timing. Nine times out of ten the slowness is one of these:
- A database query without an index, especially on a growing table
- N+1 queries inside a loop
- A synchronous call to a third-party API with no connection pooling
- Loading an entire S3 object into memory when you only need the header
- Cold start on a JVM or .NET function with a huge dependency tree
Move SDK client initialization outside the handler. Reuse the client across invocations. That alone saves hundreds of milliseconds on every warm call.
Alternative fixes when raising the timeout isn't enough
Use async invocation for anything user-facing
API Gateway caps its integration timeout at 29 seconds. If your Lambda takes 45 seconds, API Gateway returns a 504 to the user while Lambda keeps running. That's a silent failure from the client's point of view.
The real fix: have the API accept the request, drop a message on SQS, return a job ID immediately, and process the work in a separate Lambda triggered by the queue. The client polls a /status/{jobId} endpoint. SQS-triggered Lambdas can run up to 15 minutes with automatic retries.
Step Functions for multi-step jobs
If your function does "fetch, transform, upload, notify," that's four steps pretending to be one. Split them. Step Functions orchestrates each step as its own Lambda with its own timeout, and the state machine can run for up to a year. You also get visibility into which step failed without digging through log streams.
Fargate or Batch for genuinely long work
Video transcoding, large PDF generation, ML inference on big inputs — these don't belong in Lambda. A Fargate task has no 15-minute ceiling. Trigger it from Lambda, hand off to ECS, and use a callback or EventBridge event to signal completion.
Provisioned concurrency for cold-start timeouts
If your timeouts only happen on cold starts (first invocation after a deploy, or after a quiet period), that's a different problem. Provisioned concurrency keeps N instances warm. Costs more, but it eliminates the cold-start tail.
One more gotcha: retries. An async Lambda invocation gets retried twice by default on failure. If your function is non-idempotent and times out after partially writing to a database, you'll get duplicate rows. Set MaximumRetryAttempts to 0 on the event source mapping, or make the handler idempotent with a request ID check.
Prevention: catch it before your users do
Create a CloudWatch alarm on the Duration metric with a threshold at 80% of your timeout, and another on the Errors metric. Point them at an SNS topic that pages you. Also, log the elapsed time of every external call in your handler. When someone asks "why is this slow?" six weeks from now, you'll have the answer in the logs instead of guessing.
And set a realistic timeout from day one. The default 3 seconds is a trap — it exists to fail fast on truly broken deploys, not as a production setting for anything that talks to a network.