You packaged your LLM app, ran serverless deploy, and AWS rejected the upload with a 250 MB unzipped limit violation. The llm sdk bundle size lambda error usually traces back to official provider SDKs pulling in axios, form-data, and polyfills that have no place in a function zip. Below is the end-to-end workflow we use to shrink a 300 MB bundle under the limit and keep it there.
Step 1: Reproduce the exact bundle limit violation
Before optimizing, confirm the failure is size, not permissions or a bad handler path. Package the function the same way your CI does, omitting dev dependencies.
npm ci --omit=dev
zip -r9 function.zip . -x '*.git*' 'test/**' '*.ts' 'src/**' '!dist/**'
unzip -l function.zip | tail -1
If the last line shows a total exceeding 262144000 bytes (250 MB unzipped), you have the llm sdk bundle size lambda error. AWS also enforces a 50 MB zipped direct upload; larger zips must go through S3. Note the unzipped number—that is the hard stop. A function that is 240 MB unzipped might zip to 40 MB and still deploy, but you are one dependency away from breaking.
Verify the measurement
Run aws lambda update-function-code --function-name my-llm-fn --zip-file fileb://function.zip. A Lambda::CodeSizeExceeded error confirms the limit. Keep this command for later regression checks after you slim the bundle.
Step 2: Identify the heavy transitive dependencies
Provider SDKs are the usual suspects. Map the disk footprint per package to see what is actually shipped:
npm ls --prod --parseable | sed 's/.*node_modules\///' | xargs -I{} du -sh node_modules/{} 2>/dev/null | sort -rh | head -20
Typical output shows @anthropic-ai/sdk at 18 MB, openai at 22 MB, and axios (pulled by both) at 12 MB. The llm sdk bundle size lambda error compounds when you support three providers and each ships its own HTTP stack plus a copy of form-data and node-fetch. Even if you only import one method, the bundler often includes the whole package unless you are aggressive with tree-shaking.
Use a tree-shaking analyzer
If you bundle with esbuild or webpack, add --metafile=meta.json and inspect with npx esbuild --analyze=meta.json. This reveals which import drags in buffer, crypto, or fs polyfills meant for browsers. In Lambda you already have those built-ins; bundling them is pure waste.
Step 3: Replace monolithic SDKs with a thin HTTP client
Delete the provider packages from package.json. If you need access to many models across vendors, route through a single OpenAI-compatible gateway. n4n.ai exposes one OpenAI-compatible endpoint that addresses 240+ models, so you can drop every vendor SDK and keep a 2 KB fetch wrapper.
// src/llm.ts
export interface ChatMsg { role: 'system' | 'user' | 'assistant'; content: string }
export async function chat(model: string, messages: ChatMsg[]) {
const res = await fetch(process.env.LLM_BASE_URL!, {
method: 'POST',
headers: {
'content-type': 'application/json',
authorization: `Bearer ${process.env.LLM_API_KEY}`,
},
body: JSON.stringify({ model, messages, stream: false }),
})
if (!res.ok) throw new Error(`LLM ${res.status}: ${await res.text()}`)
return (await res.json()) as { choices: { message: ChatMsg }[] }
}
This removes openai, @anthropic-ai/sdk, and their transitive deps. Your package.json should only list typescript as a dev dependency and maybe zod for runtime validation. The llm sdk bundle size lambda error disappears because there is no SDK to bundle—just fetch, which Node 18+ provides natively.
Step 4: Bundle with esbuild and mark AWS built-ins external
Do not ship aws-sdk (Node 18+ includes it in the runtime). Bundle only your code and true runtime deps.
npx esbuild src/index.ts --bundle --platform=node --target=node18 \
--external:aws-sdk --external:@aws-sdk/* \
--format=cjs --outfile=dist/index.js --metafile=meta.json
Then measure the artifact:
zip -r9 dist.zip dist package.json -x '*.ts'
unzip -l dist.zip | tail -1
A healthy bundle is under 5 MB unzipped. If you still see bloat, check meta.json for accidental inclusion of tslib or source-map-support. Add --minify to esbuild to cut another 20–30% if needed.
Handle ESM-only packages
Some LLM utilities ship ESM only. Add --main-fields=module,main and --conditions=import to esbuild. If a dep breaks, add it to --external and rely on Lambda’s Node layers, but that risks the llm sdk bundle size lambda error returning if the layer is large. Prefer finding a CJS-compatible alternative or writing the small wrapper yourself.
Step 5: Configure Serverless or SAM to package the slim artifact
For Serverless Framework, disable automatic node_modules inclusion so only your dist and package.json go in:
# serverless.yml
package:
individually: true
patterns:
- '!node_modules/**'
- 'dist/**'
- 'package.json'
If you use serverless-webpack, set:
// webpack.config.js
module.exports = {
mode: 'production',
target: 'node',
externals: ['aws-sdk', '@aws-sdk/*'],
optimization: { minimize: true },
}
SAM users should set AWS::Serverless::Function PackageType: Zip and use the aws-sam-cli esbuild build method. Always run npm prune --omit=dev before packaging so no test runner or TypeScript compiler slips in.
Verify packaging locally
Run serverless package and inspect .serverless/my-fn.zip. Unzip to a temp dir and run du -sh .. If it exceeds 250 MB, the llm sdk bundle size lambda error will block deploy; fix before pushing. A good target is under 30 MB unzipped to leave headroom for future deps.
Step 6: Deploy and confirm runtime behavior
Deploy with your normal pipeline. After success, invoke the function with a minimal payload:
aws lambda invoke --function-name my-llm-fn \
--payload '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"ping"}]}' \
out.json
cat out.json
Check CloudWatch Logs for the response. If you see MODULE_NOT_FOUND, you externalized a dep that is not in the Lambda runtime—re-bundle it. If you see a timeout, the cold start is fine; the llm sdk bundle size lambda error is gone, but you may need to raise memory or trim a stray import.
Regression guard
Add a CI step that fails if the zip exceeds a threshold:
SIZE=$(unzip -l dist.zip | tail -1 | awk '{print $1}')
if [ "$SIZE" -gt 50000000 ]; then echo "Bundle too large"; exit 1; fi
This catches the llm sdk bundle size lambda error before it hits AWS and wastes a deploy minute. Set the threshold at 50 MB zipped, well under the 250 MB unzipped ceiling, to force discipline.
Step 7: Optional—use Lambda layers for shared code
If you run multiple functions, a layer with the fetch wrapper is fine, but keep the layer under 250 MB total. Do not put provider SDKs in the layer “to clean up the function zip”—that just moves the llm sdk bundle size lambda error to the layer limit and slows all functions with a large init. Layers are best for shared small utilities, not for avoiding the real fix.
What success looks like
Your dist.zip is under 10 MB zipped and under 30 MB unzipped. serverless deploy completes without CodeSizeExceeded. The function cold starts in under 400 ms on 256 MB. You deleted 200+ MB of node_modules. The llm sdk bundle size lambda error is no longer in your backlog.
Keep the bundle lean by reviewing npm ls after every dependency add. A new @company/llm-tool that pulls puppeteer will silently reintroduce the problem; the CI size guard stops it. Measure, cut SDKs, bundle tight, package explicit, verify invoke—that is the whole cycle.