withastro/astro · error · ActionError

CONTENT_TOO_LARGE

CONTENT_TOO_LARGE

Error message

Request body exceeds ${bodySizeLimit} bytes

What it means

Astro enforces security.actionBodySizeLimit (default 1 MB) on action request bodies. When an incoming request carries a content-length header larger than the configured limit, the size is known before reading, so parseRequestBody rejects immediately with ActionError code CONTENT_TOO_LARGE — the body is never parsed.

Solutions

  1. Raise the limit in astro.config.mjs: security: { actionBodySizeLimit: 10 * 1024 * 1024 } (10 MB in this example)
  2. Reduce the payload: strip unneeded fields, paginate, or compress client-side
  3. Move large uploads to a dedicated server endpoint (src/pages/api/*.ts) which has no action limit, and keep the action for metadata

Example fix

// before — astro.config.mjs defaults (1 MB), payload is 3 MB
// after
export default defineConfig({
  security: {
    actionBodySizeLimit: 10 * 1024 * 1024, // 10 MB
  },
});
Defensive patterns

Strategy: validation

Validate before calling

// check payload size before sending
const LIMIT = 1024 * 1024; // keep in sync with security.actionBodySizeLimit
const body = JSON.stringify(payload);
if (body.length > LIMIT) {
  throw new Error(`Payload ${body.length}B exceeds action limit ${LIMIT}B — use an endpoint`);
}
await actions.importData(payload);

Try / catch

try {
  await actions.importData(payload);
} catch (e) {
  if (e instanceof ActionError && e.code === 'CONTENT_TOO_LARGE') {
    // split the payload into chunks or move to a dedicated upload endpoint
  } else throw e;
}

Prevention

When it happens

Trigger: POSTing an action payload (JSON or form) whose declared content-length exceeds the limit — e.g. base64-encoded images or large text in a JSON action, or a multipart upload above 1 MB against the default.

Common situations: Adding image/file upload-through-actions features; embedding large generated content (reports, pasted documents) in action inputs; adapters or proxies that inflate the body (compression removed) pushing sizes just over the default.

Related errors


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/3a1937d86ce1b46c. Report an issue: GitHub.

Appendix: source

Thrown at packages/astro/src/actions/runtime/server.ts:267

	if (ctx.routePattern === ACTION_RPC_ROUTE_PATTERN) {
		return { from: 'rpc', name: ctx.url.pathname.replace(/^.*\/_actions\//, '') } as const;
	}
	const queryParam = ctx.url.searchParams.get(ACTION_QUERY_PARAMS.actionName);
	if (queryParam) {
		return { from: 'form', name: queryParam } as const;
	}
	return undefined;
}

async function parseRequestBody(request: Request, bodySizeLimit: number) {
	const contentType = request.headers.get('content-type');
	const contentLengthHeader = request.headers.get('content-length');
	const contentLength = contentLengthHeader ? Number.parseInt(contentLengthHeader, 10) : undefined;
	const hasContentLength = typeof contentLength === 'number' && Number.isFinite(contentLength);

	if (!contentType) return undefined;
	if (hasContentLength && contentLength > bodySizeLimit) {
		throw new ActionError({
			code: 'CONTENT_TOO_LARGE',
			message: `Request body exceeds ${bodySizeLimit} bytes`,
		});
	}
	try {
		if (hasContentType(contentType, formContentTypes)) {
			if (!hasContentLength) {
				const body = await readBodyWithLimit(request.clone(), bodySizeLimit);
				const formRequest = new Request(request.url, {
					method: request.method,
					headers: request.headers,
					body: toArrayBuffer(body),
				});
				return await formRequest.formData();
			}
			return await request.clone().formData();
		}
		if (hasContentType(contentType, ['application/json'])) {

View on GitHub (pinned to 52e6c34790)