AWS Lambda Payload Too Large? The 6MB Limit Explained + Real Workarounds That Work
The 6 MB payload cap on a synchronous Lambda invocation cannot be raised — not with a support ticket, not with a service quota request, not with anything. If your request or response is bigger than that, the fix isn't a bigger number, it's a different architecture: hand the client a presigned S3 URL, or stream the response instead of buffering it. Here's the part almost nobody mentions when they explain the 6 MB rule: it's measured in MiB, not decimal megabytes, so the real wall sits at exactly 6,291,456 bytes — and if you're compressing your payload to sneak under it through API Gateway, that trick does nothing, because API Gateway silently decompresses your request before Lambda ever sees the compressed version.
Jake found this out the hard way on a Thursday afternoon. A customer dropped off a phone with a cracked screen and asked him to pull photos off it before he sent it out for repair — forty-odd 4K videos, bundled into one API call his cousin's app made to a Lambda function Jake had never looked at twice. The call died instantly. Not slow, not a timeout. Dead on arrival, with an error that didn't mention "too big" anywhere in it.
"It says RequestEntityTooLargeException," Jake said, reading it off his screen like it was in another language. "Nothing's too large. It's a phone backup."
First: figure out which side is actually too large
"Payload too large" is Lambda's way of saying "something crossed a 6 MB line," but there are two completely different lines, and the fix for one does nothing for the other.
- The request is too large. You're calling Lambda synchronously — through API Gateway, a Lambda function URL, the AWS SDK's
Invokecall, or an Application Load Balancer — and what you're sending in is bigger than the limit. You'll usually seeRequestEntityTooLargeExceptionor an HTTP413. - The response is too large. Your function ran fine, did the work, and then choked trying to hand the result back. This one is sneakier, because the failure looks like it's coming from inside your own code — a JSON serialization error, or Lambda just quietly returning an error about the body it tried to send.
🙋♂️ Jake's Reality Check
"So which one is mine? The error just says the payload is too large, it doesn't say which end."
Look at where the error fires. If your client (the app, the browser, curl, Postman) throws the error before your Lambda code even starts logging — check CloudWatch, you'll see no invocation at all — that's the request. If CloudWatch shows your function ran to completion and the error shows up after, on the way back to the caller, that's the response.
The exact numbers behind "payload too large"
Lambda's own documentation includes a small note that explains a lot of confusion: it uses "MB" to mean 1,024 KB, i.e. what's technically a mebibyte (MiB), not a clean decimal megabyte. So when a limit says "6 MB," the real ceiling is 6 × 1,024 × 1,024 bytes — 6,291,456 bytes. That's why a file that reads as "6.0 MB" on your desktop (which usually uses decimal MB) can still get rejected; your operating system and Lambda aren't counting the same way.
| Path into or out of Lambda | Limit | Can it be raised? |
|---|---|---|
| Synchronous invoke — request | 6 MB | No |
| Synchronous invoke — buffered response | 6 MB | No |
Streamed response (function URLs / InvokeWithResponseStream) |
200 MB | No, but it's a different mechanism |
| Asynchronous (Event) invoke | 1 MB | No |
| Request line + headers combined (any invocation type) | 1 MB | No |
| Lambda behind an Application Load Balancer | 1 MB (request and response) | No |
| API Gateway (REST or HTTP API), before it even reaches Lambda | 10 MB | No |
| Deployment package (.zip, uploaded via API/console) | 50 MB zipped / 250 MB unzipped | No — use S3 or a container image beyond this |
⚠️ The trap that catches experienced engineers
Notice the last row. "Payload too large" doesn't always mean an invocation payload. If your error is about the deployment package — your zip file, when you run aws lambda update-function-code — that's a completely different 50 MB/250 MB limit, and nothing in this article about requests or responses will fix it. The fix there is to upload the zip to S3 first and deploy from the S3 object, or switch to a container image, which supports up to 10 GB.
Fixing a request that's too large
The honest fix here is a mindset change: stop sending the actual file to Lambda. Lambda is compute, not a delivery truck. If a client needs to hand you something bigger than a few megabytes — a video, a scanned document, a database export — the right move is to let the client put that object directly into S3, and only tell Lambda where it landed.
The presigned-URL upload flow
- The client asks your backend (a small, low-payload Lambda call, or an authenticated endpoint) for permission to upload.
- Your backend generates a presigned
PUTURL for an S3 object — a temporary, signed link that lets that one client upload that one object, without ever needing AWS credentials of their own. - The client uploads the file directly to S3 using that URL. This traffic never touches Lambda, so the 6 MB limit never applies to it.
- S3 fires an event (or the client makes a small follow-up call) that tells Lambda "the object is at this key." Lambda's actual invocation payload is now just a string — a bucket name and key — nowhere near 6 MB.
- Lambda reads the object from S3 when it needs to process it, using the S3 SDK, not the Invoke API, so the same 6 MB ceiling doesn't apply there either.
✅ Why this is the one to use
Every other workaround — chunking, compression, raising memory — still keeps Lambda's Invoke API as the delivery mechanism for your data, which means you're always going to bump into some limit again as files grow. Presigned S3 uploads remove the file from the invocation entirely. It also happens to be cheaper: you're not paying Lambda duration for the seconds it takes to receive megabytes of bytes it's just going to write straight to S3 anyway.
Ethan's take is blunt about the alternative here. "People try to fight the 6 MB limit with base64 encoding, gzip, splitting the file into three API calls — and every one of those is more code than just handing S3 a presigned URL. You're not outsmarting the limit. You're building a worse, slower version of what S3 already does for free."
Files bigger than 5 GB: presigned URLs per part
A single presigned PUT URL wraps one plain S3 PutObject call, and a single PutObject is capped at 5 GB — so a browser upload using one presigned URL runs into a ceiling of its own well before it ever reaches Lambda. For anything bigger, S3's own answer is multipart upload: the object gets split into parts of 5 MiB to 5 GiB each, up to 10,000 parts, for a maximum object size around 5 TB. You can presign each part's UploadPart call individually, so the client still never needs AWS credentials — it just makes more than one signed request instead of one. AWS's general guidance is to start considering multipart upload once an object passes roughly 100 MB, well before you hit the hard 5 GB wall.
Fixing a response that's too large
The same logic runs in reverse. If your function generates a big report, a rendered image, a zipped export, or query results with thousands of rows, don't try to squeeze all of that back through the Invoke response. You have two real options, and they solve different problems.
| Option | Use it when |
|---|---|
| Write result to S3, return the key/URL | The result is a file the client will download or store — a PDF, an export, a rendered image. |
| Response streaming (up to 200 MB) | The client needs the data live, in the response body itself — a long generated document, a large API response the client parses immediately, or you want faster time-to-first-byte. |
Response streaming: what it actually fixes
Lambda functions can stream a response back to the caller through a Lambda function URL, through the InvokeWithResponseStream API, or through an API Gateway proxy integration configured for streaming. Instead of buffering the whole response in memory and sending it in one block, your function sends chunks as they're ready — and the payload ceiling jumps from 6 MB to 200 MB.
There's a bandwidth rule worth knowing before you plan around it: the first 6 MB of a streamed response has no speed cap at all. Past that first 6 MB, Lambda throttles the rest of the stream to a maximum of 2 MBps. So a 50 MB streamed response isn't instant — the tail end of it is deliberately rate-limited.
⚠️ What this actually breaks
Response streaming only fixes the response. It does nothing for a request that's too large going in — that's still capped at 6 MB with no streaming equivalent for uploads through Invoke. It also natively supports Node.js managed runtimes only; Python, Java, and other languages need a custom runtime with a Runtime API integration, or a tool like the Lambda Web Adapter, to stream. And streamed invocations bill for the function's full duration, even if the client's connection drops partway through — the function keeps running and you keep paying, so don't pair it with an unnecessarily long timeout.
The API Gateway compression trap
This is the counterintuitive part from the top of this article, worth spelling out fully because it's genuinely popular advice, and it's genuinely wrong for this specific problem.
API Gateway supports payload compression. By default, it will automatically decompress an incoming request if the client sends a Content-Encoding header — that part is even on by default, no configuration needed. You can also turn on compression for the response side by setting a minimumCompressionSize on your API, anywhere from 0 to 10 MB.
Here's the catch: API Gateway decompresses a compressed request before it hands the body to your Lambda integration. Your function never sees the compressed bytes. It sees the full, decompressed payload — and that's what gets checked against Lambda's 6 MB limit. So if a client gzips a 20 MB JSON file down to 4 MB and sends that through API Gateway, API Gateway happily accepts it (it's under the 10 MB API Gateway ceiling), decompresses it back to 20 MB, and Lambda rejects it anyway.
🕐 What compression is actually good for here
- Cutting transfer time and data costs between the client and API Gateway.
- Getting a large-but-under-6-MB payload past API Gateway's separate 10 MB limit, if the uncompressed size still fits Lambda's ceiling.
- What it will never do: raise the number of bytes Lambda is willing to accept once decompressed.
Function URLs, API Gateway, and ALB: three different limits stacked on top of each other
Whatever sits in front of your Lambda function adds its own ceiling on top of Lambda's own 6 MB rule — and your request has to clear all of them, not just one.
| Front door | Its own limit | Then Lambda still applies |
|---|---|---|
| Function URL (direct) | None of its own | 6 MB request / 6 MB or 200 MB response (streaming) |
| API Gateway (REST or HTTP API) | 10 MB | 6 MB request / 6 MB or 200 MB response (streaming) |
| Application Load Balancer | Header limits only, no separate body limit | 1 MB request AND 1 MB response — ALB's own Lambda-target limit, tighter than Lambda's default |
That last row surprises people who assumed "Lambda's limit is 6 MB" applies everywhere. It doesn't. When Lambda sits behind an Application Load Balancer as a target, the load balancer integration itself caps both the request body and the response JSON at 1 MB — a full sixth of what a direct Invoke call or a function URL allows, and it can't be raised either.
Async invocations and the 1 MB ceiling
Switching an invocation from synchronous to asynchronous (InvocationType: Event) doesn't buy you more room — it buys you less. Asynchronous invocations, the kind used by S3 event notifications, EventBridge rules, and SNS-triggered functions, are capped at 1 MB, a sixth of the synchronous request limit.
This trips people up specifically because async invocations are often triggered by another AWS service rather than called directly, so the payload isn't something you typed — it's whatever S3 or EventBridge decided to put in the event. Usually that's small (an S3 event notification is just metadata about an object, not the object itself), but if you're constructing large custom payloads and firing them asynchronously on purpose — batch job parameters, for instance — you can hit this 1 MB wall well before you'd ever see it on a synchronous call.
🙋♂️ Jake's Reality Check
"So async is supposed to be the 'fire and forget, don't wait around' option, and it's actually got a smaller mailbox than the one that waits for a reply?"
Yes, and it's not an accident. Async invocations get queued internally so Lambda can retry them if your function fails. AWS keeps that queue's payload size small on purpose — a smaller cap keeps the retry queue itself lean and predictable at scale. If you need to fire-and-forget something big, put the big part in S3 and pass the S3 key in the event, same pattern as the sync case.
When S3 genuinely isn't an option: chunking and pagination
Sometimes you don't control the client. A third-party webhook is going to POST a big blob at your endpoint whether or not you'd rather it use a presigned URL, and you can't hand a payment processor or a partner API a set of upload instructions to follow. For those cases, there are two honest fallbacks — honest in the sense that both add real complexity, and neither is as clean as just using S3.
Chunk it on the sender's side, reassemble on yours
If you control the sending system even partially, split the payload into pieces well under 6 MB, send each as a separate invocation carrying a chunk index and a shared job ID, and reassemble in a Lambda function that writes each chunk to S3 (or DynamoDB, for smaller pieces) as it arrives, keyed by that job ID. A final chunk, or an explicit "done" marker, triggers the actual processing step. This is real engineering effort — you're building your own multipart protocol — so reach for it only after presigned S3 URLs are genuinely off the table.
Paginate large query results instead of returning them all at once
If your "response too large" case is actually thousands of database rows, the fix usually isn't a bigger payload at all — it's not fetching them all in one call. Return a page of results plus a continuation token, and let the client ask for the next page. This is the same pattern DynamoDB, S3's ListObjectsV2, and most AWS APIs already use for exactly this reason, and it tends to make the whole system faster, not just compliant with a size limit.
Orchestrating big, multi-file jobs without ever touching the payload limit
The chunking pattern above works, but it's you rebuilding coordination logic by hand — tracking which chunks arrived, what happens if one fails, when to trigger the final step. If the job is "process thousands of files" rather than "receive one big file," AWS Step Functions has a purpose-built tool for this: the Map state, set to Distributed mode.
A Distributed Map state can read an item list straight out of an S3 bucket — either a list of object keys, or even the rows of a single large CSV file sitting in S3 — and fan that out into child workflow executions, each one invoking a Lambda function on a slice of the data. None of that data ever passes through a single oversized Lambda payload; each child invocation only ever sees its own small slice. By default, Step Functions runs up to 10,000 of these child executions in parallel, and each has its own execution history, so a failure in one doesn't take down the batch.
✅ Why this is worth the setup
Hand-rolled chunking works for one big file arriving once. Distributed Map earns its keep the moment you're processing a batch of files or a dataset that keeps growing — it hands you retries, concurrency control, and per-item error handling that you'd otherwise be writing yourself, on top of a pattern that was already designed around not stuffing large data through a single Lambda invocation.
Ethan doesn't reach for this one by default, though. "Distributed Map is the right call when 'the file' is really thousands of files, or one file you need to process row by row at scale. If you genuinely just have one video or one PDF, that's still a presigned URL and a single function — don't build an orchestration layer to solve a one-file problem."
Working with the file once it's out of the invocation payload
Once you've moved the actual file to S3 and Lambda's invocation is just carrying a key, the payload limit stops being your constraint — but you'll still bump into Lambda's storage limits while you're processing that file inside the function itself. /tmp is configurable from 512 MB up to 10,240 MB (10 GB), in 1 MB increments, and it's local, ephemeral disk that doesn't persist between invocations. For files bigger than that, or for cases where multiple invocations need to share a working directory, Lambda can mount an Amazon EFS file system, which isn't capped the way /tmp is.
✅ The adjacent task most people need thirty seconds later
Once you're downloading from S3 inside your function instead of receiving the file as a payload, do it with the SDK's streaming download rather than pulling the whole object into memory first, especially if you've bumped /tmp up close to 10 GB — your function's own memory allocation (also configurable, up to 10,240 MB) needs enough headroom for both, or you'll trade a payload error for an out-of-memory one.
What Jake's shop actually changed
The app Jake's cousin built was sending the whole video backup as a base64-encoded string inside the request body of a single Lambda call — which, on top of everything else, means it was paying a roughly 33% size penalty just from the base64 encoding itself before it even got near the 6 MB wall. Ethan's fix was the presigned-URL flow from earlier: the app now asks a small "give me an upload URL" Lambda function for permission first, uploads the video straight to S3, and only then fires a tiny Lambda call that says "process the file at this key." The customer's phone got backed up before the shop closed. The lost Saturday it cost Jake the week before — a customer who walked out annoyed and didn't come back for the screen repair either — is the number that actually made the case for spending an afternoon on the fix.
What to do when nothing here fixes it
Be honest with yourself about one thing: there is no support ticket, no quota increase request, no enterprise support plan that raises Lambda's 6 MB synchronous limit. It is a hard limit, not a soft one, and AWS documents it that way deliberately — it protects the service's ability to scale rapidly for everyone using it, not just you. If you've read this far and none of the fixes apply because your workload genuinely needs to pass tens of megabytes through a single synchronous call with no S3 hop in between, the honest answer is that Lambda's Invoke API is the wrong tool for that specific call, not that there's a setting you haven't found yet. That usually means: an ECS or Fargate task behind the same API Gateway route for that one heavy endpoint, while everything else stays on Lambda; or accepting the extra round trip that a presigned URL costs, which is almost always smaller than the cost of the workaround you'd otherwise build.
Frequently asked questions
What does "413" or "RequestEntityTooLargeException" actually mean?
It means the request body Lambda received was bigger than 6 MB for a synchronous call (or 1 MB for asynchronous, or 1 MB if you're behind an Application Load Balancer). It's not a code bug — it's Lambda rejecting the invocation before your function code ever runs, which is why you won't see any corresponding entry in your function's own CloudWatch logs.
Can I increase the 6 MB Lambda payload limit?
No. Unlike quotas such as concurrent executions, the invocation payload size is a hard limit AWS documentation explicitly marks as not increasable, for any invocation type, through any request process.
When should I use Step Functions instead of just chunking manually?
When "the payload" is really a batch — thousands of files, or one large dataset you're processing row by row. A Distributed Map state fans that work out to many small Lambda invocations for you, with built-in retries and concurrency, instead of you hand-building your own chunk-tracking logic. For a single oversized file, a presigned S3 URL alone is simpler and is usually all you need.
Is the limit different for API Gateway HTTP APIs vs REST APIs?
No. AWS's own service quota reference lists a single 10 MB payload limit that applies to both REST APIs and HTTP APIs (anything that isn't a WebSocket API), and it isn't adjustable for either type. Whatever gets past that 10 MB gate still has to clear Lambda's own 6 MB limit afterward.
Why did my payload work at 5.9 MB but fail at 6.05 MB?
Because "6 MB" in Lambda's documentation means 6 × 1,024 × 1,024 bytes (6,291,456 bytes), not a clean 6,000,000. A file your file explorer reports as "6.0 MB" using decimal megabytes is already over that real ceiling. Check the actual byte count, not the rounded label your OS shows you.
Does gzip compression let me send more than 6 MB to Lambda?
No, and this is the single most common mistake in this whole topic. If you're going through API Gateway, it decompresses your request before handing it to Lambda, so Lambda always checks the uncompressed size. Compression can help you get a payload past API Gateway's separate 10 MB gate, but it never raises what Lambda itself is willing to accept.
What's the payload limit for Lambda behind an Application Load Balancer?
1 MB, for both the request body and the response JSON — noticeably tighter than the 6 MB you'd get invoking the same function directly or through API Gateway. It isn't adjustable.
What's the payload limit for asynchronous (Event) invocations?
1 MB. This is the invocation type used for things like S3-triggered and EventBridge-triggered functions, and its limit is a sixth of the synchronous limit, not larger, which surprises people who assume "asynchronous" implies more room.
Does response streaming help with large request payloads?
No. Response streaming, and the 200 MB limit that comes with it, only applies to what your function sends back. A request coming in is still capped at 6 MB with no streaming option for uploads through the Invoke API — for large uploads, use a presigned S3 URL instead.
Which programming languages support response streaming?
It's natively supported on Lambda's Node.js managed runtimes. Other languages, including Python and Java, need a custom runtime with a custom Runtime API integration, or a tool like the Lambda Web Adapter, to stream a response.
Does response streaming cost more?
It doesn't have a different pricing model, but it changes your risk: you're billed for the function's full duration, and a streamed invocation isn't stopped if the caller's connection drops partway through, so it keeps running and billing regardless. Keep the function's timeout sized to the work, not padded out "just in case."
What's the difference between the invocation payload limit and the deployment package size limit?
They're unrelated. The invocation payload limit (6 MB/1 MB/200 MB, covered throughout this article) is about the data going into or out of a running function. The deployment package limit (50 MB zipped, 250 MB unzipped) is about the size of your function's own code and dependencies when you deploy it. An error about one has nothing to do with the other.
How do I upload a file bigger than 5 GB using a presigned URL?
A single presigned URL for a plain PutObject call is capped at 5 GB, the same as any single S3 PUT. Past that, use S3 multipart upload — split the object into 5 MiB–5 GiB parts, generate a separate presigned URL for each part's UploadPart call, and complete the upload once every part has arrived. Objects handled this way can reach roughly 5 TB.
How long does a presigned S3 URL stay valid?
You set the expiration when you generate it. Using AWS credentials or the SigV4 signing process directly, it can be set for up to 7 days. Shorter windows — minutes to an hour — are more typical for one-time client uploads and downloads.
What if my client can't generate a presigned URL, like a webhook from a third party?
Then presigned URLs aren't available to you for that specific inbound path, and you're into the chunking, pagination, or Distributed Map fallbacks covered above — all real solutions, all more work than the S3 approach, so reserve them for cases where you genuinely don't control the sender.
What's the actual maximum file size Lambda can process if I use S3 and /tmp or EFS?
S3 objects themselves can reach roughly 5 TB via multipart upload. Inside your function, /tmp gives you up to 10,240 MB (10 GB) of configurable, ephemeral local storage, and mounting Amazon EFS removes that cap for larger or shared working files. None of this is limited by the 6 MB invocation payload rule, because the file itself never travels through the Invoke API.
Revision note. Written September 2026. These are hard limits that rarely move, but if AWS ever changes them, this page will be updated to match. If you're staring at a failed invocation right now, take a breath — this one has a clean fix, and you'll have it running again well before the day is out.