Return a short, generic message and a stable error code, and keep the detail in your server logs. Production GraphQL APIs should not return stack traces or run in debug mode. OWASP lists excessive error messages among the insecure defaults to change, because they teach an attacker how your server works.
Why errors are a leak
A GraphQL response can carry an errors array even when the HTTP status is a success. Servers often fill it with whatever the exception said: a file path, a SQL fragment, a library version, an internal service name. Each one shortens an attacker's research.
OWASP's GraphQL cheat sheet says production APIs should not return stack traces or be in debug mode, and notes that the way to do it is implementation specific. Middleware that formats errors is one popular approach. The cheat sheet gives one example: with one common server library you pass an option that turns off stack traces in error responses.
The same cheat sheet advises rejecting invalid input gracefully, being careful not to reveal excessive information about how the API and its validation work.
What clients need, what logs need
| Field | Client response | Server log |
|---|---|---|
| Short human message | Yes, generic | Yes |
| Stable error code | Yes | Yes |
| Stack trace | No | Yes |
| SQL or file path | No | Yes, if safe to store |
| Request ID | Yes | Yes |
| User or tenant ID | No | Yes |
The request ID is the bridge. A user can quote it to support, and an engineer can find the full detail in logs without the user ever seeing it.
Steps to fix
- Find your error formatter or equivalent hook in the GraphQL server library you use.
- Map known, safe errors to explicit codes such as
UNAUTHENTICATED,FORBIDDENandBAD_USER_INPUT. - Replace everything else with a generic internal error message plus a request ID.
- Turn off debug mode and stack trace output in production configuration, not just in code defaults.
- Make sure validation errors do not echo schema details beyond what a client needs. Related risk: field suggestions, covered in this post on introspection and suggestions.
- Log the detail server side with sensitive values removed.
- Test by causing failures on purpose in a staging environment: a malformed query, a missing field, a resolver exception, a database timeout. Read each response.
Common traps
Resolver exceptions from a database driver often include query text. Third party API failures may include URLs and tokens in their messages. Frameworks sometimes add an extensions object with debugging data. Check all three.
Also look at partial responses. GraphQL can return some data plus errors for the rest. Make sure fields that failed authorization return the same shape of error as fields that do not exist, so the response does not confirm that a hidden field is real.
Finally, separate logging from responses in your tests. A common mistake is to fix the response and leave the verbose error in a log that many people can read. Treat log access as a control too.
Try it yourself
Click through it at your own pace, or open it full screen.
Look for findings about verbose errors or information disclosure and note how they are described.
Quick check
Is it fine to show full errors in staging?
Usually yes, but staging is often reachable from the internet and holds real looking data. Apply the same rules unless the environment is private.
How do I help users report an error without leaking detail?
Return a request ID in the response. Support can quote it and engineers can find the full detail in logs.
Safeguard scans your dependencies and SBOMs for known vulnerabilities, including GraphQL server libraries, so you can patch the code that formats these errors. A free scan is at gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.