What it means
Lambda caps how much data can cross the invocation boundary in either direction, and the two limits are far apart. A synchronous invocation may carry 6 MB in and 6 MB out. An asynchronous one may carry 256 KB. Both are hard service limits: they are not adjustable quotas, and no support request will move them.
The gap between the two is where most surprises live. The same payload that works perfectly through a synchronous call fails immediately when the same function is invoked from EventBridge, SNS, or with InvocationType=Event — a twenty-fourfold reduction in headroom that arrives with a change that looks purely architectural. Moving a call from synchronous to asynchronous is normally a scaling improvement, and this is the one way it can silently break.
Direction is worth reading carefully, because it changes where to look. A request that is too large never reaches your function: there is no START line, no logging of yours, nothing in the log group for that attempt at all, and the error surfaces at whatever tried to invoke you. A response that is too large means the handler ran to completion — the query ran, the rows were assembled, the work was paid for — and only then was the result rejected. That second shape is particularly frustrating because the invocation shows every sign of success right up until it fails.
The limits also have a habit of being reached long after the code is written. An endpoint returning every order for a customer is comfortable at a hundred orders and fine at a thousand. Somewhere past that it crosses six megabytes, and the failure lands on the largest, most valuable customers first. That is why pagination or a claim-check pointer is the durable answer rather than trimming fields to buy headroom — the size of the response should not depend on how much data exists behind it.
CloudWatch’s Errors metric: Whether CloudWatch’s Errors metric counts this depends on whether the error escaped your handler, which the log line alone does not settle. Counted when the error escapes the handler — which is the usual case on the response side, since the runtime cannot deliver an oversized return value. LogStitch classified the example below from its log level rather than an extracted error type.
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
6 raw lines, in the order CloudWatch delivered them.
LogStitch After
The same lines, grouped into the invocation they belong to.
Error7a4c1e93-0b25-4f68-8d31-9e6b2a5c7f04
- 17:12:08.114PLAT
START RequestId: 7a4c1e93-0b25-4f68-8d31-9e6b2a5c7f04 Version: $LATEST
- 17:12:08.118INFO
INFO Export requested range=2026-07-01..2026-07-31
- 17:12:10.244INFO
INFO Assembled 41882 rows
- 17:12:10.402ERROR
ERROR RequestEntityTooLargeException: Request must be smaller than 6291456 bytes for the InvokeFunction operation
- 17:12:10.588PLAT
END RequestId: 7a4c1e93-0b25-4f68-8d31-9e6b2a5c7f04
- 17:12:10.588PLAT
REPORT RequestId: 7a4c1e93-0b25-4f68-8d31-9e6b2a5c7f04 Duration: 2474.11 ms Billed Duration: 2475 ms Memory Size: 1024 MB Max Memory Used: 412 MB Status: error
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
The byte count in the message tells you which limit you hit. 6291456 is 6 MiB, the synchronous invocation limit, and it applies to the request going in and the response coming back. 262144 is 256 KiB, the asynchronous limit, which is far smaller and catches people who moved a working synchronous call to an event-driven one.
Direction matters and the wording distinguishes it. A message about the request means the event was too big to deliver and your code never ran, so there will be no START line for it at all. A message about the response payload means your handler ran, did the work, and the return value could not be delivered — which appears after all of your own logging.
Causes, most likely first
The response contains a whole result set
Check what the handler returns on the failing path. An unpaginated query, a full export, or a list endpoint with no limit grows with the data behind it and eventually crosses 6 MB — a threshold that arrives suddenly, long after the code was written.
A file is being passed through the function
Look for base64 in the payload. Encoding inflates binary data by about a third, so a 5 MB file becomes roughly 6.7 MB in the event and exceeds the limit before anything processes it.
The invocation is asynchronous and the limit is 256 KB, not 6 MB
Check the invocation type. An event-source mapping, an EventBridge rule, an SNS subscription or an InvocationType=Event call all use the asynchronous path, whose limit is roughly twenty-four times smaller. A payload that worked when invoked synchronously fails immediately when the same call is made asynchronously.
The function is passing full payloads between steps
Look at whether one function invokes another with the accumulated data. Chained functions that hand entire result sets along accumulate size at every hop, and the limit is reached somewhere in the middle of a pipeline rather than at either end.
Fixes
Pass a pointer, not the payload
Write the data to S3 and pass the key. This is the standard pattern for anything that can grow — often called the claim-check pattern — and it removes the size ceiling from the request path entirely.
js// Instead of returning the whole export in the response:
const key = `exports/${crypto.randomUUID()}.json`;
await s3.send(new PutObjectCommand({
Bucket: process.env.EXPORT_BUCKET,
Key: key,
Body: JSON.stringify(rows)
}));
return {
statusCode: 200,
body: JSON.stringify({
key,
url: await getSignedUrl(s3, new GetObjectCommand({
Bucket: process.env.EXPORT_BUCKET, Key: key
}), { expiresIn: 900 }),
count: rows.length
})
};
Paginate rather than returning everything
A limit and a cursor keep the response bounded regardless of how much data exists behind it, and they stop the endpoint from getting slower and larger as the table grows.
jsconst PAGE = 100;
const result = await doc.send(new QueryCommand({
TableName: "orders",
KeyConditionExpression: "customerId = :c",
ExpressionAttributeValues: { ":c": customerId },
Limit: PAGE,
ExclusiveStartKey: event.queryStringParameters?.cursor
? JSON.parse(Buffer.from(event.queryStringParameters.cursor, "base64").toString())
: undefined
}));
return {
items: result.Items,
cursor: result.LastEvaluatedKey
? Buffer.from(JSON.stringify(result.LastEvaluatedKey)).toString("base64")
: null
};
Stream the response instead of buffering it
A response-streaming function writes to the client as it goes rather than assembling a single return value, which lifts the response ceiling well beyond 6 MB. It applies to Function URLs and direct invocations, not to every integration, so check the caller supports it.
jsexport const handler = awslambda.streamifyResponse(
async (event, responseStream) => {
for await (const chunk of rows()) {
responseStream.write(JSON.stringify(chunk) + "\n");
}
responseStream.end();
}
);
Upload directly to S3 rather than through the function
For file uploads, a presigned URL lets the client send the object straight to S3. The function issues the URL and never touches the bytes, so the payload limit stops being relevant at all.
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.
Related errors
Errors that show up alongside this one, or that people mistake for it.
References
- AWS Lambda Developer Guide — Lambda quotas
- AWS Lambda Developer Guide — Configuring a Lambda function to stream responses
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.