Validating Error Responses
An error response deserves the same careful scrutiny as a success response — the right status code, a genuinely useful body, and no leaked internals.
What you'll learn
- Design assertions for an error response's status, structure, and message content
- Identify when an error response leaks internal implementation details
- Explain why consistent error shape across endpoints matters for API consumers
Prerequisites
Explanation
A success response gets carefully checked against a schema, exact field values, and status code. An error response, in far too many test suites, gets a single lazy check: "is the status not 200?" That's a real gap — error responses are exactly as testable, and exactly as capable of hiding real defects, as success responses are.
A well-designed error response deserves three specific checks. The status code should be the correct, specific one for the situation (from earlier lessons: 400 for bad input, 401 for missing authentication, 403 for missing authorization, 404 for a resource that doesn't exist, 409 for a conflict like the stale-data case from the previous lesson) — not a generic 400 or 500 for every possible failure. The body's structure should be consistent and machine-readable: does every endpoint's error response use the same shape (say, { "error": { "code": "...", "message": "..." } }), or does one endpoint return a plain string while another returns a nested object? Inconsistency here quietly breaks any client code trying to handle errors generically across the whole API. The message content should be genuinely useful to whoever's debugging — specific enough to act on ("email is required") rather than generic to the point of uselessness ("an error occurred").
There's a security dimension too, echoing the defect-reporting lesson from Software Testing Foundations: an error response should never leak internal details a real attacker could use — a full stack trace, an internal file path, a raw database error message, or (subtly) confirmation of whether a specific username or email exists in the system just from how the error is worded differently for "wrong password" versus "no such account." A tester validating error responses is doing double duty: confirming the API is genuinely useful to legitimate callers, and confirming it isn't accidentally useful to attackers.
Example
A simulated error response checked for status, structure, message quality, and the absence of leaked internals.
const errorResponse = {
status: 400,
body: { error: { code: "MISSING_FIELD", message: "email is required" } },
};
function isWellFormedError(res) {
const hasCorrectStatus = res.status >= 400 && res.status < 500;
const hasStructuredBody = typeof res.body?.error?.code === "string" && typeof res.body?.error?.message === "string";
const leaksInternals = /stack|trace|\/usr\/|select /i.test(JSON.stringify(res.body));
return hasCorrectStatus && hasStructuredBody && !leaksInternals;
}
console.log(isWellFormedError(errorResponse)); // trueTry it yourself
Change the message to something that leaks a stack trace and re-run to see the check fail.
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Guided exercise
Guided exercise
Two endpoints return errors in different shapes: endpointA returns { error: { code, message } }, endpointB returns just a plain string. Set isConsistentShape to reflect whether these two endpoints use a consistent error structure.
Checks: correctly identifies the inconsistency
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Independent exercise
Independent exercise
Write a function leaksInternalDetails(errorBody) that returns true if JSON.stringify(errorBody) case-insensitively contains any of: 'stack trace', a file path pattern like '/usr/' or 'c:\\', or 'select ' (a leaked SQL query fragment).
Checks: a clean error message is not flagged · a leaked stack trace and path are flagged · a leaked SQL query is flagged
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Common mistakes
- Checking only that an error response's status code is "not 200," without verifying it's the specific, correct error code for the situation.
- Using a different error body shape on different endpoints, breaking any client code that tries to handle errors generically.
- Never testing error responses for leaked internal details, treating that concern as security-team-only rather than something any API tester can check.
Knowledge check
Takeaway
Error responses deserve the same rigor as success responses: the specific correct status code, a consistent structured body, a genuinely useful message, and no leaked internal details.
Summary
This lesson covered validating error responses for correct status codes, consistent structure across endpoints, and the absence of leaked internal implementation details.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.