xiaohongshu
xiaohongshu
exception-taxonomy
Hierarchical exception system with HTTP status codes, machine-readable error codes, and structured responses for consistent API error handling across all endpoints.
Full skill instructions
Hierarchical exception system with HTTP status codes, error codes, and structured responses for consistent API error handling.
A well-designed exception taxonomy provides:
The hierarchy typically follows:
BaseAppError (abstract)
├── AuthenticationError (401)
├── AuthorizationError (403)
├── ResourceError (404/409)
├── ValidationError (422)
├── RateLimitError (429)
├── ExternalServiceError (502/503)
└── PaymentError (402)
from dataclasses import dataclass, field
from typing import Optional, Dict, Any
from enum import Enum
class ErrorCode(str, Enum):
"""Standardized error codes for API responses."""
# Authentication
AUTH_INVALID_CREDENTIALS = "AUTH_INVALID_CREDENTIALS"
AUTH_TOKEN_EXPIRED = "AUTH_TOKEN_EXPIRED"
AUTH_TOKEN_INVALID = "AUTH_TOKEN_INVALID"
AUTH_EMAIL_EXISTS = "AUTH_EMAIL_EXISTS"
# Authorization
FORBIDDEN = "FORBIDDEN"
# Resources
RESOURCE_NOT_FOUND = "RESOURCE_NOT_FOUND"
RESOURCE_CONFLICT = "RESOURCE_CONFLICT"
# Rate Limiting
RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED"
# External Services
GENERATION_FAILED = "GENERATION_FAILED"
GENERATION_TIMEOUT = "GENERATION_TIMEOUT"
# Validation
VALIDATION_ERROR = "VALIDATION_ERROR"
INVALID_STATE_TRANSITION = "INVALID_STATE_TRANSITION"
@dataclass
class BaseAppError(Exception):
"""Base exception for all application errors."""
message: str
code: ErrorCode
status_code: int = 500
details: Optional[Dict[str, Any]] = field(default_factory=dict)
retry_after: Optional[int] = None
def __post_init__(self):
super().__init__(self.message)
def to_dict(self) -> Dict[str, Any]:
"""Convert to API response format."""
error_dict = {
"error": {
"message": self.message,
"code": self.code.value,
}
}
if self.details:
error_dict["error"]["details"] = self.details
if self.retry_after is not None:
error_dict["error"]["retry_after"] = self.retry_after
return error_dict
@dataclass
class NotFoundError(BaseAppError):
"""Resource not found error."""
resource_type: str = "resource"
resource_id: str = ""
message: str = field(init=False)
code: ErrorCode = field(default=ErrorCode.RESOURCE_NOT_FOUND)
status_code: int = 404
def __post_init__(self):
self.message = f"{self.resource_type.title()} not found"
self.details = {
"resource_type": self.resource_type,
"resource_id": self.resource_id,
}
super().__post_init__()
@dataclass
class RateLimitError(BaseAppError):
"""Rate limit exceeded error."""
retry_after: int = 60
message: str = "Rate limit exceeded"
code: ErrorCode = field(default=ErrorCode.RATE_LIMIT_EXCEEDED)
status_code: int = 429
def __post_init__(self):
self.details = {"retry_after": self.retry_after}
super().__post_init__()
@dataclass
class InvalidStateTransitionError(BaseAppError):
"""Invalid state transition error."""
current_status: str = ""
target_status: str = ""
message: str = field(init=False)
code: ErrorCode = field(default=ErrorCode.INVALID_STATE_TRANSITION)
status_code: int = 409
def __post_init__(self):
self.message = f"Cannot transition from '{self.current_status}' to '{self.target_status}'"
self.details = {
"current_status": self.current_status,
"target_status": self.target_status,
}
super().__post_init__()
export enum ErrorCode {
AUTH_INVALID_CREDENTIALS = 'AUTH_INVALID_CREDENTIALS',
AUTH_TOKEN_EXPIRED = 'AUTH_TOKEN_EXPIRED',
AUTH_TOKEN_INVALID = 'AUTH_TOKEN_INVALID',
FORBIDDEN = 'FORBIDDEN',
RESOURCE_NOT_FOUND = 'RESOURCE_NOT_FOUND',
RESOURCE_CONFLICT = 'RESOURCE_CONFLICT',
RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED',
VALIDATION_ERROR = 'VALIDATION_ERROR',
INVALID_STATE_TRANSITION = 'INVALID_STATE_TRANSITION',
}
interface ErrorDetails {
[key: string]: unknown;
}
export class BaseAppError extends Error {
constructor(
public readonly message: string,
public readonly code: ErrorCode,
public readonly statusCode: number = 500,
public readonly details: ErrorDetails = {},
public readonly retryAfter?: number
) {
super(message);
this.name = this.constructor.name;
}
toJSON() {
const error: Record<string, unknown> = {
message: this.message,
code: this.code,
};
if (Object.keys(this.details).length > 0) {
error.details = this.details;
}
if (this.retryAfter !== undefined) {
error.retry_after = this.retryAfter;
}
return { error };
}
}
export class NotFoundError extends BaseAppError {
constructor(resourceType: string, resourceId: string) {
super(
`${resourceType.charAt(0).toUpperCase() + resourceType.slice(1)} not found`,
ErrorCode.RESOURCE_NOT_FOUND,
404,
{ resource_type: resourceType, resource_id: resourceId }
);
}
}
export class RateLimitError extends BaseAppError {
constructor(retryAfter: number = 60) {
super(
'Rate limit exceeded',
ErrorCode.RATE_LIMIT_EXCEEDED,
429,
{ retry_after: retryAfter },
retryAfter
);
}
}
export class InvalidStateTransitionError extends BaseAppError {
constructor(currentStatus: string, targetStatus: string) {
super(
`Cannot transition from '${currentStatus}' to '${targetStatus}'`,
ErrorCode.INVALID_STATE_TRANSITION,
409,
{ current_status: currentStatus, target_status: targetStatus }
);
}
}
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(BaseAppError)
async def app_error_handler(request: Request, exc: BaseAppError) -> JSONResponse:
headers = {"Retry-After": str(exc.retry_after)} if exc.retry_after else None
return JSONResponse(
status_code=exc.status_code,
content=exc.to_dict(),
headers=headers,
)
@app.exception_handler(Exception)
async def generic_error_handler(request: Request, exc: Exception) -> JSONResponse:
logger.exception(f"Unexpected error: {exc}")
return JSONResponse(
status_code=500,
content={"error": {"message": "An unexpected error occurred", "code": "INTERNAL_ERROR"}},
)
@router.get("/jobs/{job_id}")
async def get_job(job_id: str, user_id: str = Depends(get_current_user)):
job = await job_service.get(job_id)
if not job:
raise NotFoundError(resource_type="job", resource_id=job_id)
if job.user_id != user_id:
raise AuthorizationError(resource_type="job")
return job
interface APIError {
error: {
message: string;
code: string;
details?: Record<string, unknown>;
retry_after?: number;
};
}
function handleAPIError(error: APIError): void {
switch (error.error.code) {
case 'AUTH_TOKEN_EXPIRED':
authStore.refreshToken();
break;
case 'RATE_LIMIT_EXCEEDED':
const retryAfter = error.error.retry_after || 60;
toast.error(`Rate limited. Try again in ${retryAfter}s`);
break;
default:
toast.error(error.error.message);
}
}
retry_afterxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience