Task timed out after 30.00 seconds

Short answer

Lambda stopped your function because it was still running when its configured timeout elapsed — N is your timeout setting, not how long the work needed. The invocation is billed for the full timeout, nothing your handler was about to return is kept, and any work already in flight is abandoned wherever it was.

What it means

Lambda gives every invocation a wall-clock budget. You set it per function, anywhere from one second to fifteen minutes, and the runtime enforces it with a hard stop: when the clock runs out, the execution environment is frozen and the invocation is marked failed. Your handler does not get an exception, a signal, or a chance to clean up. It simply stops existing between one instruction and the next.

That is why the number in the message is so often misread. Task timed out after 30.00 seconds does not mean the work took thirty seconds — it means your timeout is thirty seconds, and the work had not finished by then. It would say the same thing if the function were one second from done or if it were hung on a socket that was never going to answer. The message describes the limit, not the workload.

Three consequences follow, and all three cost money or correctness. You are billed for the full timeout, because the environment was reserved the whole time. Whatever your handler was about to return is discarded. And any side effect already in flight — a half-written batch, an open transaction, a message pulled from a queue but not yet deleted — is left exactly where it was, which is how a timeout on a retryable event source turns into duplicate work on the retry.

The distinction that actually matters when you are debugging is stuck versus slow. A stuck function is waiting on something that will never answer, and its logs stop dead at the call that hung. A slow function is making progress and simply runs out of road, and its logs keep advancing right up to the last millisecond. The fix for one is a timeout on the downstream call; the fix for the other is more time, more memory, or less work per invocation. Reading the last few lines before the cutoff tells you which one you have, and that is the whole reason it matters to see those lines grouped with their own invocation rather than interleaved with every other execution that was running at the same moment.

Counts

CloudWatch’s Errors metric: The error escaped your handler, so Lambda reports the invocation as failed and CloudWatch’s Errors metric counts it. LogStitch classified the invocation above as uncaught.

What it looks like in CloudWatch

This is the shape the failure arrives in: the lines of one invocation scattered among everything else the log group received at the same moment.

CloudWatch Logs Before

11 raw lines, in the order CloudWatch delivered them.

2026-08-29T09:47:02.311Z START RequestId: 5e8fc611-9265-4166-de50-60718293af06 Version: $LATEST
2026-08-29T09:47:02.314Z 5e8fc611-9265-4166-de50-60718293af06 INFO Starting nightly reconciliation
2026-08-29T09:47:02.402Z 5e8fc611-9265-4166-de50-60718293af06 INFO Connected to RDS proxy
2026-08-29T09:47:02.688Z 5e8fc611-9265-4166-de50-60718293af06 INFO Fetched 24118 ledger rows
2026-08-29T09:47:05.109Z 5e8fc611-9265-4166-de50-60718293af06 DEBUG Reconciled 4000 / 24118
2026-08-29T09:47:11.664Z 5e8fc611-9265-4166-de50-60718293af06 DEBUG Reconciled 8000 / 24118
2026-08-29T09:47:20.902Z 5e8fc611-9265-4166-de50-60718293af06 WARN Downstream ledger API slow p99=2841ms
2026-08-29T09:47:28.517Z 5e8fc611-9265-4166-de50-60718293af06 DEBUG Reconciled 12000 / 24118
2026-08-29T09:47:32.311Z 5e8fc611-9265-4166-de50-60718293af06 Task timed out after 30.00 seconds
2026-08-29T09:47:32.311Z END RequestId: 5e8fc611-9265-4166-de50-60718293af06
2026-08-29T09:47:32.311Z REPORT RequestId: 5e8fc611-9265-4166-de50-60718293af06 Duration: 30000.00 ms Billed Duration: 30000 ms Memory Size: 256 MB Max Memory Used: 241 MB Status: timeout

LogStitch After

The same lines, grouped into the invocation they belong to.

Timeout5e8fc611-9265-4166-de50-60718293af06dur 30.00sbilled 30.00smem 241/256MBlogs 11
  1. 09:47:02.311PLAT
    START RequestId: 5e8fc611-9265-4166-de50-60718293af06 Version: $LATEST
  2. 09:47:02.314INFO
    INFO	Starting nightly reconciliation
  3. 09:47:02.402INFO
    INFO	Connected to RDS proxy
  4. 09:47:02.688INFO
    INFO	Fetched 24118 ledger rows
  5. 09:47:05.109DEBUG
    DEBUG	Reconciled 4000 / 24118
  6. 09:47:11.664DEBUG
    DEBUG	Reconciled 8000 / 24118
  7. 09:47:20.902WARN
    WARN	Downstream ledger API slow p99=2841ms
  8. 09:47:28.517DEBUG
    DEBUG	Reconciled 12000 / 24118
  9. 09:47:32.311
    5e8fc611-9265-4166-de50-60718293af06 Task timed out after 30.00 seconds
  10. 09:47:32.311PLAT
    END RequestId: 5e8fc611-9265-4166-de50-60718293af06
  11. 09:47:32.311PLAT
    REPORT RequestId: 5e8fc611-9265-4166-de50-60718293af06	Duration: 30000.00 ms	Billed Duration: 30000 ms	Memory Size: 256 MB	Max Memory Used: 241 MB	Status: timeout
Timeout
Status
30.00s
Duration
30.00s
Billed
241/256MB
Memory
6%
Headroom
No
Cold start
TaskTimeout
Error type

The panel on the right is generated by running the excerpt on the left through the same parser that powers the free web stitcher — it is what the tool actually produces for this input, not an illustration of it.

How to confirm it from the logs

Look for three things together. The Task timed out line is stamped with the request ID and appears instead of a normal handler return. The REPORT line for that same request ID shows a Duration equal to your configured timeout — 30000.00 ms for a 30-second timeout, to the millisecond — and on newer runtimes carries Status: timeout. And there is no END-then-result pattern: the last thing your own code logged is wherever it got to, which is the single most useful clue about what was slow.

The giveaway that separates a timeout from a crash: the duration is suspiciously round. A function that crashes stops at an arbitrary millisecond. A function that times out stops at exactly the number you configured.

Causes, most likely first

1

A downstream call has no timeout of its own, so it hangs until Lambda's clock runs out

How to confirm

Find the last line your handler logged before the timeout and check what comes next in your code. If it is a network call — an HTTP request, a database query, an SDK call — and there is no log line after it, that call never returned. Most SDK and HTTP clients default to a timeout longer than a typical Lambda's, so the Lambda dies first and the call never gets to report its own failure.

2

The function is in a VPC and cannot reach the endpoint at all, so the connection hangs

How to confirm

Check whether the function is VPC-attached and whether the address it is calling is public. A VPC-attached Lambda in a subnet with no NAT gateway and no VPC endpoint has no route to the internet, and the symptom is not a refusal — it is silence, until the timeout. ETIMEDOUT in the logs a few seconds before the timeout points the same way.

3

The work genuinely takes longer than the timeout allows

How to confirm

Look at your own progress logging across several invocations. If the function logs steady progress right up to the moment it dies — Reconciled 12000 / 24118 and then nothing — it is not stuck, it is just not finished. Compare the rate against the remaining work: at that pace, would it ever have completed?

4

A held connection or an unresolved promise keeps the event loop alive after the work is done

How to confirm

Check whether your logs show the work completing and the invocation still timing out. On Node, an open database pool or a pending promise keeps the event loop busy, and Lambda waits for it. If you see your final log line and then a timeout, this is it — not a slow downstream.

Fixes

Fix 1

Set an explicit timeout on every downstream call, shorter than the function's

This is the fix that turns a mystery into an error message. Give every network call a timeout comfortably below the Lambda's own, so the call fails inside your handler and you get a stack trace naming the culprit instead of a bare Task timed out.

For the AWS SDK v3, set it on the request handler. The values below leave a 25-second function around 20 seconds of headroom.

jsimport { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { NodeHttpHandler } from "@smithy/node-http-handler";

const client = new DynamoDBClient({
  requestHandler: new NodeHttpHandler({
    connectionTimeout: 1000,   // give up on the TCP connect fast
    requestTimeout: 5000       // and on a hung response
  }),
  maxAttempts: 3
});
Fix 2

Use the remaining-time budget the runtime already hands you

Every Lambda runtime gives the handler the milliseconds it has left. Check it before starting an expensive step and bail out deliberately — a controlled error you log is worth far more than a timeout, because it tells you where the function was and how much time it thought it had.

jsexport const handler = async (event, context) => {
  for (const batch of batches) {
    // Stop while there is still time to write a useful log line.
    if (context.getRemainingTimeInMillis() < 5000) {
      console.warn("Out of budget", {
        processed: done,
        remaining: batches.length - done
      });
      return { done, incomplete: true };
    }
    await process(batch);
  }
};
Fix 3

Raise the timeout only once you know the work is bounded

Raising the timeout is the right fix when the function is genuinely doing more work than it has time for — and the wrong one when it is hung, because it converts a 30-second failure into a 15-minute one at 30× the cost. Confirm from the logs that the function makes steady progress before you change the number. Lambda's maximum is 900 seconds (15 minutes).

yaml# AWS SAM
Resources:
  ReconcileFunction:
    Type: AWS::Serverless::Function
    Properties:
      Timeout: 120        # seconds; hard maximum is 900
      MemorySize: 1024    # more memory also means more CPU
Fix 4

Split the work so a single invocation cannot outgrow its budget

If the work grows with your data, no timeout is high enough for long. Move to a pattern where each invocation handles a bounded slice — an SQS queue with a batch size, a Step Functions map, or a paginated self-invocation that carries a cursor. The goal is that the runtime of one invocation stops depending on how much total work there is.

Also seen as

The same underlying failure, worded differently by a different runtime, SDK version, or logging layer. All of these land here — there is no separate page for each phrasing.

Task timed out after 3.00 secondsTask timed out after 900.00 secondsStatus: timeoutlambda function timing out2026-01-01T00:00:00.000Z abcdef01-2345-6789-abcd-ef0123456789 Task timed out after 6.00 seconds

Errors that show up alongside this one, or that people mistake for it.

References

LogStitch finds this automatically, across every invocation in your account.

Paste a log excerpt into the free web stitcher and see it grouped, classified, and measured in your browser — nothing is uploaded. Or run the Mac app against your own AWS profiles and get the same view over every function you own.