Large requests or messages fail at some hop you didn't know had a limit
“Small payloads work. A big batch import, report response or chat message fails with 413, a gRPC RESOURCE_EXHAUSTED, a WebSocket closed with 1009, or a socket that just drops, and it works locally where nothing sits in between.”
ERROR CODES YOU MAY SEE
A code is a clue. Use its meaning and surrounding evidence to narrow the cause.
413HTTP status codes · RFC 9110 · standardThe server refuses to process the request because its content is larger than it is willing or able to process (Content Too Large).
Find which hop rejected it: a proxy body limit (nginx client_max_body_size defaults to 1m), the load balancer, a serverless payload cap, or the app's body parser. Raising every limit is rarely the fix for large files; upload directly to object storage instead.
Official reference for 413 (opens in new tab)Code definition reviewedRESOURCE_EXHAUSTEDgRPC status codes · standardSome resource has been exhausted (status 8). The gRPC library itself returns it when a sent or received message is larger than the configured limit or flow-control limits are reached; applications also use it for exhausted quotas.
Read the status message: "received message larger than max" means a message-size limit, not load. Most implementations default to a 4 MB receive limit; paginate or stream instead of only raising it on both ends.
Official reference for RESOURCE_EXHAUSTED (opens in new tab)Code definition reviewed1009WebSocket close codes · RFC 6455 · standardThe endpoint is closing the connection because it received a message that is too big for it to process.
Find which hop has the smallest limit: the server library (ws maxPayload), a gateway (API Gateway WebSocket APIs cap messages at 128 KB and frames at 32 KB), or a proxy. Send references to large data instead of the data.
Official reference for 1009 (opens in new tab)Code definition reviewedECONNRESETNode.js system errors · platformThe peer forcibly closed the connection.
Correlate client, proxy, and server logs. A reset alone does not reveal whether an operation completed before the connection ended.
Official reference for ECONNRESET (opens in new tab)Code definition reviewed
THE PRINCIPLE
The effective limit is the smallest one on the path, and limits are a defense. Design for bounded payloads instead of chasing the next cap.
FIRST MOVES
- Find which hop rejected it: the status code and which server produced the error body (CDN page, gateway JSON, nginx HTML, app log). If the app never logged the request, it died upstream.
- List the path's limits and check current docs, because they change. As of late 2026: nginx client_max_body_size defaults to 1m; API Gateway REST and HTTP APIs cap payloads at 10 MB; WebSocket APIs at 128 KB per message and 32 KB per frame (larger closes with 1009); Lambda at 6 MB synchronous request and response, 1 MB asynchronous; Cloudflare request bodies at 100 MB on Free and Pro.
- For gRPC, most implementations reject received messages over 4 MB by default with RESOURCE_EXHAUSTED; grpc-go's send side is effectively unlimited, so the failure shows up on the receiver.
- Bound responses with pagination or server streaming, and requests with batches of N items, each idempotent so a failed chunk can be retried alone.
- Move bulk bytes out of the message: upload or download through presigned object-storage URLs and pass a reference (key, ID) through the API or socket.
TOOLS: THEN → NOW
PATTERN SNAPSHOT
// Chunks far below the smallest limit on the path; one key per chunk so each retries alone.
const MAX_BYTES = 512 * 1024;
async function importRows(rows: Row[], importId: string) {
let chunk: Row[] = [], size = 0, n = 0;
const flush = async () => {
await api.post("/imports/rows", { importId, rows: chunk }, { idempotencyKey: `${importId}:${n++}` });
chunk = []; size = 0;
};
for (const row of rows) {
const bytes = new TextEncoder().encode(JSON.stringify(row)).length;
if (chunk.length && size + bytes > MAX_BYTES) await flush();
chunk.push(row); size += bytes;
}
if (chunk.length) await flush();
}CLOSE THE AI. EXPLAIN THIS.
A client sends 7 MB JSON to API Gateway in front of Lambda. Which limit rejects it, and how would you redesign the endpoint so the size no longer matters?HOW IT WORKS UNDERNEATH
RELATED SYMPTOMS
SOURCES
- AWS Lambda quotas (invocation payload) (opens in new tab)Checked
- Amazon API Gateway: Quotas for WebSocket APIs (opens in new tab)Checked
- Cloudflare Workers: Limits (request body size by plan) (opens in new tab)Checked
- grpc-go package documentation (MaxCallRecvMsgSize default 4MB) (opens in new tab)Checked
Guide reviewed