version: "1.0.0" name: api-error-design description: "API error design patterns. An extension skill for doc-writer/mock-tester that provides error code systems, error response structures, client-friendly error messages, error catalog construction, and retry/fallback strategies. Use when designing API error handling systems involving 'API error design', 'error codes', 'error responses', 'error catalogs', 'error messages', 'retry strategies', etc. Note: actual error handling code implementation is outside the scope of this skill."
API Error Design — API Error Design Patterns
A reference for error systems, message design, and recovery strategies used by the doc-writer and mock-tester agents when designing API error documentation and tests.
Target Agents
doc-writer — Applied when writing error reference documentationmock-tester — Applied when designing error scenario tests
Error Code System Design
Hierarchical Error Code Structure
{DOMAIN}_{CATEGORY}_{DETAIL}
Examples:
AUTH_TOKEN_EXPIRED -- Authentication > Token > Expired
ORDER_PAYMENT_DECLINED -- Order > Payment > Declined
USER_VALIDATION_EMAIL -- User > Validation > Email
Error Code Catalog by Domain
Authentication/Authorization (AUTH)
| Code | HTTP | Message | Client Action | |
|---|
| AUTH_REQUIRED | 401 | Authentication is required | Redirect to login page | |
| AUTH_TOKEN_EXPIRED | 401 | Token has expired | Attempt token refresh | |
| AUTH_TOKEN_INVALID | 401 | Invalid token | Re-authenticate | |
| AUTH_FORBIDDEN | 403 | You do not have permission for this action | Guide user to request permissions | |
| AUTH_ACCOUNT_LOCKED | 403 | Account is locked. Please retry after 15 minutes | Display wait timer | |
| AUTH_INVALID_CREDENTIALS | 401 | Invalid email or password | Prompt re-entry | |
Validation (VALIDATION)
| Code | HTTP | Message | Field-Level Detail | |
|---|
| VALIDATION_REQUIRED | 422 | Required field is missing | The {field} field is required | |
| VALIDATION_FORMAT | 422 | Invalid format | Please enter a valid {type} | |
| VALIDATION_RANGE | 422 | Value is out of range | Please enter a value between {min} and {max} | |
| VALIDATION_UNIQUE | 409 | Value is already in use | This {field} is already registered | |
| VALIDATION_LENGTH | 422 | Length limit exceeded | Please enter no more than {max} characters | |
Resource (RESOURCE)
| Code | HTTP | Message | |
|---|
| RESOURCE_NOT_FOUND | 404 | The requested resource was not found | |
| RESOURCE_ALREADY_EXISTS | 409 | Resource already exists | |
| RESOURCE_DELETED | 410 | Resource has been deleted | |
| RESOURCE_LOCKED | 423 | Resource is locked | |
Rate Limit
| Code | HTTP | Message | |
|---|
| RATE_LIMIT_EXCEEDED | 429 | Request limit exceeded. Please retry after {retryAfter} seconds | |
Server (SERVER)
| Code | HTTP | Message | |
|---|
| SERVER_INTERNAL | 500 | A server error occurred. Please retry shortly | |
| SERVER_MAINTENANCE | 503 | Service is under maintenance | |
| SERVER_UPSTREAM | 502 | Failed to connect to an external service | |
Error Response Structure Standard
Basic Structure
json
{
"error": {
"code": "AUTH_TOKEN_EXPIRED",
"message": "Token has expired",
"detail": "The access token has expired. Please refresh using your refresh token.",
"timestamp": "2025-03-15T09:30:00Z",
"requestId": "req_abc123def456",
"path": "/api/v1/users/me"
}
}
Validation Error (Per-Field)
json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Input values are invalid",
"errors": [
{
"field": "email",
"code": "VALIDATION_FORMAT",
"message": "Please enter a valid email address",
"value": "invalid-email"
},
{
"field": "password",
"code": "VALIDATION_LENGTH",
"message": "Password must be at least 8 characters",
"constraint": { "min": 8 }
}
]
}
}
Error Message Writing Principles
Three Elements of a Good Error Message
- What went wrong — Describe the problem
- Why it went wrong — Explain the cause or constraint
- How to fix it — Provide a specific action
Do / Don't
| Don't | Do | |
|---|
| "Error occurred" | "Failed to create the order" | |
| "Invalid input" | "Price must be a number greater than 0" | |
| "Server error: NullPointerException at..." | "A server error occurred. Please retry shortly" | |
| "Access denied" | "You do not have permission to modify this order. Please contact an administrator" | |
| "Duplicate key constraint violation" | "This email is already registered" | |
Multilingual Error Message Structure
json
{
"error": {
"code": "VALIDATION_REQUIRED",
"message": "Required field is missing",
"messageKey": "error.validation.required",
"params": { "field": "email" }
}
}
Retry/Fallback Strategy
Retry Eligibility
| HTTP Status | Retryable? | Strategy | |
|---|
| 408 | Yes | Retry immediately | |
| 429 | Yes | Wait per Retry-After header | |
| 500 | Yes (conditional) | Exponential backoff | |
| 502, 503, 504 | Yes | Exponential backoff | |
| 400, 401, 403, 404 | No | Client-side fix required | |
| 409, 422 | No | Input correction required | |
Exponential Backoff
wait_time = min(baseDelay * 2^attempt + jitter, maxDelay)
Example: baseDelay=1s, maxDelay=30s
Attempt 1: 1s + random(0~500ms)
Attempt 2: 2s + random(0~500ms)
Attempt 3: 4s + random(0~500ms)
Attempt 4: 8s + random(0~500ms)
Maximum 3-5 attempts
Error Test Scenario Matrix
| Category | Test Case | Expected Code | |
|---|
| No authentication | Request without Authorization header | 401 | |
| Expired token | Request with expired JWT | 401 | |
| No permission | Access another user's resource | 403 | |
| Non-existent ID | Query with random UUID | 404 | |
| Missing required field | Remove required field from body | 422 | |
| Invalid format | "abc" in email field | 422 | |
| Duplicate creation | POST same data twice | 409 | |
| Bulk requests | Exceed Rate Limit | 429 | |
| Malformed JSON | Send {invalid json | 400 | |
| Empty body | Content-Length: 0 | 400 | |