How to debug Next.js Server Actions

Updated 28 October 2026 · 5 min read

A Server Action looks like a function call, but it is an HTTP request in disguise. Once you know what that request looks like, Server Actions are much easier to debug.

What a Server Action sends

Calling a Server Action, from a form or from code, makes the browser send a POST to the current page's URL with a Next-Action header that identifies the action. The body holds the serialized arguments (form data for forms), and the response is an RSC payload (text/x-component). In the browser's Network tab, look for a POST to the page itself.

Where its logs and errors go

The action runs on the server, so console.log inside it prints in the terminal, not the browser. In development an error thrown inside it shows in the error overlay. In production the message is hidden from the client, so return errors as data instead of throwing for anything the user should see:

'use server';

export async function createTask(formData: FormData) {
  const title = String(formData.get('title') ?? '');
  if (!title) return { error: 'Title is required' };

  await db.task.create({ data: { title } });
  revalidatePath('/tasks');
  return { ok: true };
}

Breakpoints

Server Actions are ordinary server code, so a breakpoint in the action pauses with the Node.js inspector (NODE_OPTIONS='--inspect' next dev) or with qaisdebugs, where the action's request, its logs and every call it makes appear together.

Common pitfalls

The page does not update

After changing data, call revalidatePath or revalidateTag, or the page keeps showing the old result.

redirect() inside try/catch

redirect() works by throwing a special error. If your catch catches it, the redirect never happens. Call redirect() after the try/catch.

Request body too large

Server Actions accept 1 MB by default. For larger uploads, raise serverActions.bodySizeLimit in next.config or upload in another way.

Trusting the input

An action is a public endpoint. Validate its arguments and check the user's permission inside it, just like an API route.