Call redirect() outside try/catch to avoid catching redirect error: 'use server'; export async function createPost(formData: FormData) { try { const post = await db.insert({ title: formData.get('title') }); } catch (error) { return { error: error.message }; } revalidatePath('/posts'); redirect('/posts'); }. redirect() throws error internally - placing inside try block catches it. Return errors as plain objects (Error instances not serializable): if (!valid) return { error: 'Validation failed', fields: { title: 'Required' } }. Use useActionState for client error handling: const [state, action] = useActionState(createPost, null); {state?.error &&
{state.error}
}. Unhandled errors don't return 500 (unlike API routes) - must catch explicitly. Production: use error boundaries for unexpected errors, Sentry for logging, return user-friendly messages. Validate all inputs with Zod before try block. Never expose internal error details to client.