Error Handling (Actions)

Gracefully handling failures without blowing up the UI.

Next.js4 min readConcept 36 of 65

To Throw or Not To Throw

When a Next.js Server Action encounters an uncaught error, it automatically triggers the nearest React Error Boundary (error.js).

While this is great for catastrophic system failures, it is terrible for expected user errors (like a taken username or an invalid credit card), because it destroys the user's form UI.

Inline Feedback

To provide a good user experience, you want the form to stay exactly where it is, preserving their typed input, while displaying a small red error message above the submit button.

To achieve this, the Server Action must *catch* the error internally and return it as a normal object.

The Safe Return Pattern

Wrap your server logic in a standard try/catch block.

If the operation succeeds, return { success: true }. If it fails, catch the error and return { error: 'Human readable message' }.

The client component can then check if (res.error) and set a local React state to display the message inline.

The Safe Return

Hover over the code lines to see how returning a standard JavaScript object prevents the dreaded Error Boundary from wiping out the user's form.

action.ts (Server)

'use server';
import { redirect } from 'next/navigation';

export async function saveData(formData) {
try {
// Simulating a database failure
await db.save(formData);
}
catch (e) {
// Return object instead of throwing!
return { error: "Save failed" };
}
// Must be OUTSIDE the try/catch!
redirect('/success');
}

page.tsx (Client)

'use client';

export default function Form() {
const [error, setError] = useState(null);

async function onSubmit(formData) {
const res = await saveData(formData);

if (res?.error) {
// Update local UI state inline
setError(res.error);
}
}

return (
<form action={onSubmit}>
<button>Submit</button>
</form>
);
}

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1Why should you avoid calling `redirect()` inside a `try/catch` block in a Server Action?
  2. 2What happens if a Server Action throws an unhandled error?

Remember this

  • Uncaught errors in Server Actions trigger error.js boundaries.
  • For form validation, use try/catch to return safe { error: string } objects.
  • Never return native Error objects to the client; they don't serialize.
  • Never put redirect() inside a try/catch block, or it will be swallowed.
  • Client components use the returned error string to update inline UI state.

Done with this concept?

Mark it complete to track your progress. No login needed.