Why Error Handling Testing Matters
When an API works perfectly, testing is straightforward. The real challenge — and where most bugs hide — is in how the API handles things going wrong. Error handling testing verifies that the API fails gracefully, provides useful information, and does not expose security vulnerabilities.
In production at companies like Google, error handling quality directly impacts debugging speed. A clear error message like “Field ’email’ must be a valid email address” saves hours compared to a generic “Bad Request.”
Error Response Structure
A well-designed error response follows a consistent format:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Must be a valid email address",
"value": "not-an-email"
},
{
"field": "age",
"message": "Must be a positive integer",
"value": -5
}
]
},
"requestId": "req_abc123",
"timestamp": "2025-12-15T10:30:00Z"
}
What to Test in Error Responses
| Aspect | What to Verify |
|---|---|
| Status code | Matches the error type (400, 401, 403, 404, 422, 500) |
| Error code | Machine-readable, consistent across the API |
| Message | Human-readable, actionable, no internal details |
| Details | Specific field-level errors when applicable |
| Request ID | Present for debugging and support tickets |
| Content-Type | Still application/json even for errors |
Categories of Error Testing
Input Validation Errors (400/422)
Test every input field with invalid data:
Missing required fields → "Field 'name' is required"
Wrong data type → "Field 'age' must be an integer"
Out of range → "Field 'age' must be between 0 and 150"
Invalid format → "Field 'email' must be a valid email"
Too long → "Field 'name' must be at most 100 characters"
Invalid enum → "Field 'role' must be one of: admin, user, viewer"
Authentication Errors (401)
| Scenario | Expected Message |
|---|---|
| No token provided | “Authentication required” |
| Expired token | “Token has expired” |
| Malformed token | “Invalid authentication token” |
| Revoked token | “Token has been revoked” |
Authorization Errors (403)
| Scenario | Expected Message |
|---|---|
| User accessing admin endpoint | “Insufficient permissions” |
| Accessing another user’s data | “Access denied” |
| Disabled feature | “Feature not available for your plan” |
Not Found Errors (404)
| Scenario | Expected Message |
|---|---|
| Invalid resource ID | “User not found” |
| Deleted resource | “Resource no longer exists” |
| Wrong endpoint | Standard 404 response |
Advanced Error Testing
Error Consistency
Verify that errors follow the same format across the entire API:
- Same JSON structure for all error responses
- Consistent error codes (not
INVALIDin one place andVALIDATION_FAILEDin another) - Consistent use of status codes (not 400 for auth errors in some endpoints and 401 in others)
Security in Error Messages
Test that error messages do NOT reveal:
- Database table names or column names
- Stack traces or file paths
- SQL queries or NoSQL operations
- Internal service names or IP addresses
- Whether a username exists (for login endpoints)
Bad: "Error: SELECT * FROM users WHERE id=42 failed: connection to postgres:5432 refused"
Good: "Unable to process request. Please try again later. Reference: req_abc123"
Error Handling Under Stress
- What happens when the database is down? (should return 503, not 500 with stack trace)
- What happens with malformed JSON body? (should return 400, not 500)
- What happens with extremely large payloads? (should return 413 Payload Too Large)
- What happens with unsupported Content-Type? (should return 415)
Internationalized Error Messages
If the API supports multiple languages, test:
- Error messages in each supported language via Accept-Language header
- Fallback to default language for unsupported locales
- Error codes remain the same regardless of language
Building an Error Test Suite
For each endpoint, create a systematic error test matrix:
POST /api/users
├── Missing 'name' → 400, VALIDATION_ERROR, "name is required"
├── Empty 'name' → 400, VALIDATION_ERROR, "name cannot be empty"
├── Name too long → 400, VALIDATION_ERROR, "name max 100 chars"
├── Invalid email → 422, VALIDATION_ERROR, "email format invalid"
├── Duplicate email → 409, CONFLICT, "email already exists"
├── No auth token → 401, AUTH_REQUIRED
├── Invalid token → 401, INVALID_TOKEN
├── User role on admin → 403, FORBIDDEN
├── Malformed JSON → 400, PARSE_ERROR
└── Wrong Content-Type → 415, UNSUPPORTED_MEDIA_TYPE
Hands-On Exercise
- Map error responses: For JSONPlaceholder, send 10 different invalid requests and document each error response format
- Check consistency: Compare error formats across different endpoints — are they consistent?
- Security audit: Look for any responses that reveal internal details
- Create an error matrix: For a hypothetical user registration endpoint, list all possible error scenarios with expected codes and messages
Key Takeaways
- Error handling testing is as important as happy path testing — it is where security vulnerabilities and poor user experience hide
- Error responses should follow a consistent structure: status code, error code, human-readable message, and field-level details
- Never expose internal implementation details in error messages — it is a security risk
- Test every input field with every type of invalid data: missing, empty, wrong type, out of range, too long, invalid format
- Build systematic error test matrices for each endpoint to ensure comprehensive coverage