error-responses
Use when returning errors from APIs. Use when exposing internal errors. Use when error responses lack structure.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when returning errors from APIs. Use when exposing internal errors. Use when error responses lack structure.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | error-responses |
| description | Use when returning errors from APIs. Use when exposing internal errors. Use when error responses lack structure. |
Never expose internal errors. Return structured, safe error responses.
Raw error messages leak implementation details, aid attackers, and confuse users. Errors should be safe, consistent, and actionable.
NEVER expose raw error messages or stack traces to clients.
No exceptions:
If errors expose internals, STOP:
// ❌ VIOLATION: Exposing internals
app.get('/users/:id', async (req, res) => {
try {
const user = await db.query('SELECT * FROM users WHERE id = $1', [req.params.id]);
res.json(user);
} catch (error) {
res.status(500).json({ message: error.message }); // Leaks!
}
});
What could leak:
"relation \"users\" does not exist" - Database schema"connect ECONNREFUSED 10.0.1.5:5432" - Internal IPs// ✅ CORRECT: Structured, safe errors
// Custom error classes
class AppError extends Error {
constructor(
public statusCode: number,
public code: string,
message: string
) {
super(message);
}
}
class NotFoundError extends AppError {
constructor(resource: string) {
super(404, 'NOT_FOUND', `${resource} not found`);
}
}
class ValidationError extends AppError {
constructor(public details: Record<string, string[]>) {
super(400, 'VALIDATION_ERROR', 'Validation failed');
}
}
// Error handler middleware
app.use((error: Error, req: Request, res: Response, next: NextFunction) => {
// Log full error internally
console.error('Error:', {
message: error.message,
stack: error.stack,
path: req.path,
method: req.method,
});
// Return safe response
if (error instanceof ValidationError) {
return res.status(400).json({
error: {
code: error.code,
message: error.message,
details: error.details,
}
});
}
if (error instanceof AppError) {
return res.status(error.statusCode).json({
error: {
code: error.code,
message: error.message,
}
});
}
// Unknown errors - never expose
res.status(500).json({
error: {
code: 'INTERNAL_ERROR',
message: 'An unexpected error occurred',
}
});
});
// Usage in routes
app.get('/users/:id', async (req, res, next) => {
try {
const user = await userService.findById(req.params.id);
if (!user) throw new NotFoundError('User');
res.json(user);
} catch (error) {
next(error); // Pass to error handler
}
});
Consistent structure for all errors:
interface ErrorResponse {
error: {
code: string; // Machine-readable: 'VALIDATION_ERROR'
message: string; // Human-readable: 'Validation failed'
details?: unknown; // Additional info (validation errors, etc.)
requestId?: string; // For support/debugging
}
}
// Examples:
// 400 Bad Request
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": {
"email": ["Invalid email format"],
"age": ["Must be at least 18"]
}
}
}
// 404 Not Found
{
"error": {
"code": "NOT_FOUND",
"message": "User not found"
}
}
// 500 Internal Error
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred",
"requestId": "req_abc123"
}
}
| Code | When to Use |
|---|---|
| 400 | Bad request, validation errors |
| 401 | Not authenticated |
| 403 | Authenticated but not authorized |
| 404 | Resource not found |
| 409 | Conflict (duplicate, state issue) |
| 422 | Unprocessable entity |
| 429 | Rate limited |
| 500 | Server error (hide details!) |
| 502 | Upstream service failed |
| 503 | Service unavailable |
Pressure: "Developers need to see the full error"
Response: Log full errors server-side. Return request IDs for correlation.
Action: { requestId: "abc123" } - developers can look up logs.
Pressure: "Only our services call this"
Response: Internal services get compromised. Logs get leaked. Protect everything.
Action: Same safe error handling everywhere.
Pressure: "Show details in dev, hide in prod"
Response: Dev code becomes prod code. Habits matter.
Action: Same handling in all environments. Use logging for debugging.
res.json({ message: error.message })All of these mean: Implement proper error handling.
| Exposed (Bad) | Safe (Good) |
|---|---|
| Database error messages | "An error occurred" |
| Stack traces | Request ID for log lookup |
| Internal paths | Generic error code |
| SQL queries | "Validation failed" |
| Variable dumps | Structured error object |
| Excuse | Reality |
|---|---|
| "Helps debugging" | Log server-side, return request ID. |
| "Internal API" | Internal gets compromised too. |
| "Development mode" | Same handling everywhere. |
| "Frontend needs details" | Return safe, structured details. |
| "It's faster" | Error handling is cheap. Breaches aren't. |
Log everything internally. Expose nothing externally.
Return consistent, structured errors with machine-readable codes and human-readable messages. Never leak stack traces, queries, or internal details. Use request IDs for debugging.
Use when writing tests. Use when test structure is unclear. Use when arrange/act/assert phases are mixed.
Use when designing or modifying APIs. Use when adding breaking changes. Use when clients depend on API stability.
Use when implementing authentication. Use when storing passwords. Use when asked to store credentials insecurely.
Use when same data is fetched repeatedly. Use when database queries are slow. Use when implementing caching without invalidation strategy.
Use when tempted to use class inheritance. Use when creating class hierarchies. Use when subclass needs only some parent behavior.
Use when acquiring multiple locks. Use when operations wait for each other. Use when system hangs without crashing.