When Body.parse(body) throws a ZodError inside an uncaught handler, most frameworks return a generic 500 Internal Server Error. The client has no idea what field was invalid, and your server logs expose the full Zod error tree — which reveals your schema structure to anyone reading logs or observing error responses. CWE-755 (Improper Handling of Exceptional Conditions) applies: a validation library that crashes the handler instead of returning a 400 teaches clients nothing useful and may leak internal schema details.
Low because the primary failure is poor error communication and log leakage rather than direct exploitation, though repeated 500s from malformed inputs can mask real errors.
Use .safeParse() to get a typed success/error result without throwing, then return an explicit 400 with the validation issues.
export async function POST(req: Request) {
const body = await req.json()
const result = Body.safeParse(body)
if (!result.success) {
return Response.json(
{ error: 'Invalid input', issues: result.error.issues },
{ status: 400 }
)
}
const user = await prisma.user.create({ data: result.data })
return Response.json(user, { status: 201 })
}
Alternatively, wrap .parse() in a try/catch and return a 422 with a sanitized error message. Avoid forwarding the raw ZodError.issues array to clients in production — it reveals your schema.
ID: ai-slop-security-theater.unenforced-validation.error-responses-handle-validation-failures
Severity: low
What to look for: For mutating handlers that invoke validation, count all validating handlers and enumerate how many wrap the validation in a try/catch OR use .safeParse( (which doesn't throw) AND return a structured 4xx response on validation failure. Handlers that let validation throw an unhandled exception (defaulting to a generic 500) fail this check.
Pass criteria: 100% of validating mutating handlers either use .safeParse( and return a 4xx for failures OR wrap .parse( in try/catch and return a 4xx. Report: "X validating handlers, Y with explicit error handling, 0 throwing unhandled."
Fail criteria: At least 1 validating handler has no try/catch around .parse( and no explicit 4xx response.
Skip (N/A) when: Project has 0 mutating handlers OR no mutating handler invokes validation.
Detail on fail: "1 handler returns 500 on validation failure: app/api/users/route.ts POST calls Body.parse(body) without try/catch — invalid input causes ZodError to leak as a 500"
Remediation: A validation failure should be a 400 Bad Request with a clear error, not an unhandled 500:
export async function POST(req: Request) {
const body = await req.json()
const result = Body.safeParse(body)
if (!result.success) {
return Response.json(
{ error: 'Invalid input', issues: result.error.issues },
{ status: 400 }
)
}
const user = await prisma.user.create({ data: result.data })
return Response.json(user, { status: 201 })
}