When an API endpoint returns "failed" as plain text or a 500 with an empty body, the frontend can't surface a useful message to the user and the client code can't branch on an error code to retry or recover. The user sees a generic "something went wrong" toast regardless of whether the cause was a validation failure, a rate limit, or a database outage, which both erodes trust and makes support triage much harder. AI coding tools mix response shapes across a codebase because each route gets generated in isolation — some return Response.json({ error: "..." }), some return new Response("failed", { status: 500 }), some return HTML error pages by default from thrown exceptions. Consistency is what makes an API usable; inconsistency forces every client call site to handle each endpoint's error format separately.
Medium because inconsistent error shapes degrade the user-facing error experience and complicate client-side handling, but they don't directly enable a security compromise.
Use a consistent error shape:
return NextResponse.json({ error: { code: 'invalid_input', message: 'Email required' } }, { status: 400 })
Deeper remediation guidance and cross-reference coverage for this check lives in the saas-error-handling Pro audit — run that after applying this fix for a more exhaustive pass on the same topic.
project-snapshot.error-handling.api-errors-return-jsonmediumreturn new Response(...) or NextResponse.json(...) with status >= 400). Check whether the response body is structured JSON ({ error: ..., code?: ..., message?: ... }) vs. plain text or empty.{ "ok": false } with no error message — clients can't surface meaningful messages."Found N error responses across M handlers; P use structured JSON. JSON rate: P/N = X%.""3 of 14 error responses return plain text or empty body; example: app/api/upload/route.ts returns 'failed' as text/plain".return NextResponse.json({ error: { code: 'invalid_input', message: 'Email required' } }, { status: 400 })