What it means
Lambda caps a zip-packaged function at 250 MiB — 262,144,000 bytes — measured after extraction, and measured across the function's own package plus every layer attached to it. Both halves of that sentence catch people out.
Measuring after extraction means compression is irrelevant. A 40 MB zip that expands to 300 MB is rejected exactly as firmly as an uncompressed one, so the instinct to compress harder buys nothing. What matters is the total size of the files on disk once Lambda has unpacked them.
Counting layers means the most popular workaround does not work. Moving a large dependency into a layer feels like it should help — the function's own package shrinks, after all — but the ceiling applies to the sum. Layers are genuinely useful for sharing a dependency set across several functions and for keeping deployments fast, and they do nothing whatsoever for this limit.
There is a second, smaller limit worth knowing because it produces a different message. A zip uploaded directly through the API is capped at 50 MB; going through S3 raises that, but the 250 MiB unzipped ceiling still applies underneath. So a package can fail one check, be moved to S3, and fail the other — which reads as the fix not working when in fact two separate limits were in play.
The durable answer depends on why the package is large. If it is a broad dependency tree of ordinary JavaScript, a bundler usually solves it outright by shipping only reachable code. If it is one genuinely enormous library — a machine-learning framework, a headless browser — no amount of trimming will fit it, and container images with their 10 GB limit are the path AWS intends for that workload rather than a concession.
Where you'll see it
Deployment output
Not in CloudWatchThis failure happens before the function runs, so nothing about it reaches CloudWatch Logs — there is no invocation, and no log group entry to find. Once the deployment succeeds and the function starts running, the rest of this index covers what you will see there.
Causes, most likely first
A large dependency tree is being shipped whole
Extract the artifact and measure what is in it by directory. Scientific and machine-learning libraries dominate this — numpy, pandas, scipy, torch, puppeteer — and a single one of them can approach the limit before your own code is counted.
Layers are being counted and were forgotten
Add the unzipped size of every attached layer to the function's own. The 250 MiB ceiling applies to the total, so moving dependencies into a layer to get under it does not work — that is the most common misunderstanding about this limit.
Development dependencies are in the package
Look for test frameworks, type definitions, linters, and source maps in the artifact. Installing without a production flag, or zipping the working directory rather than a build output, routinely doubles the size with things the function never loads.
The build output and the source are both present
Check for both src/ and dist/ in the artifact, or .ts files alongside their compiled .js. A packaging step that collects the project root rather than the build directory ships everything twice.
Fixes
Bundle and tree-shake instead of shipping node_modules
A bundler follows the imports that are actually reachable and emits only those. For a typical function this is the difference between tens of megabytes and hundreds of kilobytes, and it shortens cold starts as a side effect.
bashnpx esbuild src/index.js \
--bundle --minify \
--platform=node --target=node22 \
--external:@aws-sdk/* \
--outfile=dist/index.js
Move to a container image when the dependencies are genuinely large
Container images have a 10 GB limit rather than 250 MiB. For anything built around a large ML or media library this is the intended path, not a workaround — the zip limit is simply the wrong shape for that workload.
yamlResources:
ReportFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
ImageUri: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/report:latest"
Find what is actually large before deciding anything
Measure rather than guess. The offender is usually one or two directories, and knowing which decides whether you need a bundler, a layer, or a container image.
bashunzip -q function.zip -d /tmp/pkg
du -sh /tmp/pkg
du -sh /tmp/pkg/node_modules/* 2>/dev/null | sort -rh | head -15
Exclude development dependencies from the artifact
Install production dependencies only, and package the build output rather than the project directory. This alone is often enough when the package is only slightly over.
bashnpm ci --omit=dev
zip -qr function.zip dist/ node_modules/ package.json \
-x '*.map' '*/test/*' '*/tests/*' '*.d.ts'
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
This one happens before there are any logs.
LogStitch reads CloudWatch, and a deployment that fails never writes to it — so this is not an error it can find for you. Once the function deploys and starts running, the free web stitcher groups its invocations in your browser, and the Mac app does the same across every function in your account.