Skip to main content

API errors return structured JSON

ab-002596 · project-snapshot.error-handling.api-errors-return-json
Severity: mediumactive

Why it matters

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.

Severity rationale

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.

Remediation

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.

Detection

  • ID: project-snapshot.error-handling.api-errors-return-json
  • Severity: medium
  • What to look for: Enumerate API route handlers. For each, find the error responses (typically return new Response(...) or NextResponse.json(...) with status >= 400). Check whether the response body is structured JSON ({ error: ..., code?: ..., message?: ... }) vs. plain text or empty.
  • Pass criteria: At least 80% of error responses use structured JSON.
  • Fail criteria: More than 20% return plain text, empty body, or HTML on error.
  • Skip (N/A) when: No API routes exist.
  • Do NOT pass when: Error responses are JSON but contain only { "ok": false } with no error message — clients can't surface meaningful messages.
  • Report even on pass: "Found N error responses across M handlers; P use structured JSON. JSON rate: P/N = X%."
  • Detail on fail: "3 of 14 error responses return plain text or empty body; example: app/api/upload/route.ts returns 'failed' as text/plain".
  • Remediation: Use a consistent error shape:
    return NextResponse.json({ error: { code: 'invalid_input', message: 'Email required' } }, { status: 400 })
    

Taxons

History