An API error should carry the correct HTTP status code, a short generic message and a request ID. It should never include stack traces, SQL, file paths, library versions or internal hostnames. Keep detail in server logs, where your team can use it and attackers cannot.
What the guidance says
OWASP's REST Security Cheat Sheet says to return generic error messages that do not reveal unnecessary detail, never to send stack traces or internal hints to clients, and to use semantically correct codes rather than defaulting to 200 or 404. OWASP API8 adds that teams should define and enforce response schemas, including error responses, so sensitive details never reach clients.
Status codes that matter for security
| Code | Use it when | What to reveal |
|---|---|---|
| 400 | Request is malformed | Which field failed, not how the parser failed |
| 401 | Credentials are missing or wrong | Nothing about whether the account exists |
| 403 | Caller is authenticated but not permitted | Nothing about the resource |
| 405 | Method is not allowed | The allowed methods, if you wish |
| 406 | No acceptable response type | Nothing extra |
| 413 | Payload too large | The limit |
| 415 | Unsupported content type | The supported types |
| 429 | Too many requests | When to retry |
| 500 | Unexpected server fault | Nothing internal, only a request ID |
The 401 and 403 pair is defined in the cheat sheet as missing or wrong credentials versus authenticated but not permitted.
Mistakes to look for
- Stack traces in 500 responses. Common when debug mode ships to production.
- Different messages for unknown user and wrong password. This lets attackers list valid accounts.
- Database errors echoed back. They teach attackers your schema.
- Success status with an error body. Monitoring and clients cannot tell.
- Inconsistent errors across versions or hosts. An old version may still leak detail.
A simple pattern
Return a small error object with a stable code, a message written for humans and a request ID. Log the full exception with that same ID. Support can find it, and the client sees nothing internal.
How to test it
Send malformed JSON, wrong types, oversized bodies, unsupported content types and unexpected methods. Read every response body. Anything that describes your internals is a finding. A spec driven scanner does much of this automatically on documented routes.
Quick check
Should I return 404 instead of 403 for other people's records?
It is a design choice. 404 avoids confirming that a record exists. Whichever you choose, apply it consistently and cover it in authorization tests.
Is it fine to show validation details?
Yes, field level messages help legitimate clients. Avoid exposing parser internals, regular expressions or server paths.
Safeguard's free scan at gold.safeguard.sh/scan covers dependency risk, including libraries that surface debug output.
Sources
Last reviewed: 19 October 2026.