TLDR: Vercel hard-caps request bodies at 4.5MB at the platform level. bodySizeLimit in next.config can't touch it. Fix: send one file per request.

The Setup

I'm building an affiliate payout tool for an ecommerce business (a supplement brand I work with).

The core flow: upload a batch of CSV exports from AEvent (their webinar platform), the app deduplicates payouts across affiliates, and spits out a clean monthly payout report.

Simple enough.

Except it wasn't.

The Wall

First prod upload: 13 CSVs, about 6.1MB total.

I clicked upload and got this gem:

"An unexpected response was received from the server."

That's it. No status code in the UI. No detail. Just… that.

My first guess was a Next.js body parser thing. So I went to next.config.ts and added:

experimental: {
  serverActions: {
    bodySizeLimit: "100mb",
  },
},

Deployed. Tried again.

Same error.

What I Tried That Didn't Work

I'll be honest — I chased the Next.js config for longer than I should have.

I double-checked the syntax. Checked the docs. Confirmed it was actually being read at build time.

It was being read. It just wasn't doing anything.

The Actual Problem

Here's the thing nobody tells you clearly: Vercel hard-caps every serverless request body at 4.5MB at the platform level.

Not at the Next.js level. At Vercel's infrastructure layer — before the request ever reaches your runtime.

So serverActions.bodySizeLimit: "100mb" only applies when you're self-hosting. On Vercel, it's irrelevant. The 413 fires before your code even wakes up, and Vercel returns… that vague client-side message instead of the real status code.

GENIUS error messaging, truly.

The Fix That Worked

One file per request.

Instead of sending all 13 files in a single server action call, the client now loops and fires one at a time:

// ❌ Before: one 6MB body → 413
await uploadFiles(allFiles);

// ✅ After: one file per request, each well under the cap
for (const f of files) {
  await uploadFile({ filename: f.name, text: await f.text() });
}

The largest single CSV was 1.4MB — well under the cap.

And this turned out to be better than the original approach: per-file progress, per-file error isolation, incremental results as each one lands. The constraint forced a cleaner design.

The Companion Gotcha

Server actions inherit the route segment config of the page that invokes them — not the action file itself.

Vercel's default function timeout is 10 seconds. If your action does heavy DB work (thousands of upserts on a cold function), that can bite you.

Fix: add this to your page file, not your action:

export const maxDuration = 60;

Easy to miss. Now you don't have to find it the hard way.

Why This Matters to Me

Platform limits and framework limits live at different layers — and they don't always tell you clearly which one you hit.

If you're building on Vercel with Next.js server actions, 4.5MB is your real ceiling. Full stop. Plan your upload flows around it from the start: chunk client-side, one item per request, keep headroom.

The cryptic error message is the trap. Don't let it send you down the wrong config rabbit hole like it did me.