Notes from a 2 AM On-Call: Next.js Server Actions Silently Swallowing Zod Errors in Production
The Failure Symptom
[02:17:43] POST /api/actions/submit 200 OK 127ms [02:17:43] Client: formState.errors = {} [02:17:43] DB: 0 rows inserted [02:17:43] Sentry: 0 exceptions
Enter fullscreen mode
Exit fullscreen mode
Our user-facing form returned success. The database wrote nothing. No errors surfaced client-side. SLA breach alerts fired 90 seconds later when the downstream ETL pipeline starved.
The actual payload that hit our Server Action:
{ "userId" : "usr_19xKlM" , "amount" : "not-a-number" , "currency" : null }
Enter fullscreen mode
Exit fullscreen mode
Zod should have rejected this immediately. It didn't.
The Red Herring
Every Next.js 15 tutorial on Server Actions shows this pattern:
// actions/submit.ts ' use server '
import { z } from ' zod '
const schema = z . object ({ userId : z . string (), amount : z . number (). positive (), currency : z .…