Error Handling & Observability skill

Implements error handling patterns, structured logging, retry strategies, circuit breakers, and graceful degradation.

by CloudAI-X·MIT license·★ 1,416 Stars on the repo·GitHub ↗

Use now

Files of Error Handling & Observability

CloudAI-X/main1 file shown
SKILL.md
Show the full text481 lines

Error Handling & Observability

When to Load
  • Trigger: Try/catch patterns, retry logic, error responses, circuit breakers, structured logging
  • Skip: No error handling or observability involved in the current task

Error Handling Workflow

Copy this checklist and track progress:

Error Handling Progress:
- [ ] Step 1: Define error taxonomy (categories and severity)
- [ ] Step 2: Implement error handling by layer
- [ ] Step 3: Set up structured logging
- [ ] Step 4: Add retry and circuit breaker patterns
- [ ] Step 5: Configure error tracking service
- [ ] Step 6: Define user-facing error messages
- [ ] Step 7: Validate against anti-patterns checklist

Error Handling Patterns by Language

JavaScript / TypeScript
// Custom error hierarchy
class AppError extends Error {
  constructor(
    message: string,
    public statusCode: number = 500,
    public code: string = "INTERNAL_ERROR",
    public isOperational: boolean = true,
  ) {
    super(message);
    this.name = this.constructor.name;
  }
}
class NotFoundError extends AppError {
  constructor(resource: string, id: string) {
    super(`${resource} with id ${id} not found`, 404, "NOT_FOUND");
  }
}
class ValidationError extends AppError {
  constructor(public errors: Record<string, string[]>) {
    super("Validation failed", 400, "VALIDATION_ERROR");
  }
}

// WRONG: Swallowing errors silently
try {
  await saveUser(data);
} catch (e) {
  // nothing here -- bug hides forever
}

// WRONG: Catching and re-throwing without context
try {
  await saveUser(data);
} catch (e) {
  throw e; // pointless try/catch
}

// CORRECT: Add context, handle or propagate
try {
  await saveUser(data);
} catch (error) {
  if (error instanceof ValidationError) {
    return res.status(400).json({ errors: error.errors });
  }
  logger.error({ err: error, userId: data.id }, "Failed to save user");
  throw new AppError("Unable to save user", 500, "USER_SAVE_FAILED");
}
Express Global Error Handler
// Centralized error handler middleware (must have 4 params)
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  if (err instanceof AppError) {
    logger.warn(
      { code: err.code, statusCode: err.statusCode, path: req.path },
      "Operational error",
    );
    return res.status(err.statusCode).json({
      error: { code: err.code, message: err.message },
    });
  }

  // Unexpected errors -- these are bugs
  logger.error({ err, path: req.path }, "Unexpected error");
  res.status(500).json({
    error: { code: "INTERNAL_ERROR", message: "An unexpected error occurred" },
  });
});
Python
# Custom exception hierarchy
class AppError(Exception):
    def __init__(self, message: str, code: str = "INTERNAL_ERROR", status: int = 500):
        self.message = message
        self.code = code
        self.status = status
        super().__init__(message)

class NotFoundError(AppError):
    def __init__(self, resource: str, id: str):
        super().__init__(f"{resource} {id} not found", "NOT_FOUND", 404)

class ValidationError(AppError):
    def __init__(self, errors: dict[str, list[str]]):
        self.errors = errors
        super().__init__("Validation failed", "VALIDATION_ERROR", 400)

# WRONG: Bare except
try:
    result = process(data)
except:  # catches SystemExit, KeyboardInterrupt too!
    pass

# CORRECT: Specific exceptions, proper logging
try:
    result = process(data)
except ValidationError as e:
    logger.warning("Validation failed", extra={"errors": e.errors})
    raise
except DatabaseError as e:
    logger.error("Database error during processing", exc_info=True)
    raise AppError("Processing failed", "PROCESS_FAILED") from e
Go
// Define sentinel errors and custom types
var (
    ErrNotFound     = errors.New("resource not found")
    ErrUnauthorized = errors.New("unauthorized")
)

type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation: %s - %s", e.Field, e.Message)
}

// WRONG: Ignoring errors
data, _ := json.Marshal(user)  // error silently dropped

// WRONG: Only returning error string
if err != nil {
    return fmt.Errorf("failed: %s", err.Error())  // loses error chain
}

// CORRECT: Wrap errors with context
if err != nil {
    return fmt.Errorf("saving user %s: %w", user.ID, err)  // %w preserves chain
}

// CORRECT: Check error types
if errors.Is(err, ErrNotFound) {
    http.Error(w, "Not found", http.StatusNotFound)
    return
}
var valErr *ValidationError
if errors.As(err, &valErr) {
    http.Error(w, valErr.Error(), http.StatusBadRequest)
    return
}

Structured Logging

JSON Log Format
// WRONG: Unstructured string logs
console.log(`User ${userId} created order ${orderId} at ${new Date()}`);
// Impossible to parse, filter, or aggregate

// CORRECT: Structured JSON logs
import pino from "pino";

const logger = pino({
  level: process.env.LOG_LEVEL || "info",
  formatters: {
    level: (label) => ({ level: label }),
  },
  redact: ["req.headers.authorization", "password", "ssn"],
});
logger.info({
  event: "order_created",
  userId: "123",
  orderId: "456",
  amount: 99.99,
  currency: "USD",
});
// Output: {"level":"info","event":"order_created","userId":"123","orderId":"456",...}
Correlation IDs
// Middleware to propagate correlation ID across requests
import { randomUUID } from "crypto";
import { AsyncLocalStorage } from "async_hooks";

const asyncStorage = new AsyncLocalStorage<{ correlationId: string }>();

app.use((req, res, next) => {
  const correlationId =
    (req.headers["x-correlation-id"] as string) || randomUUID();
  res.setHeader("x-correlation-id", correlationId);

  asyncStorage.run({ correlationId }, () => next());
});

// Logger automatically includes correlation ID
function getLogger() {
  const store = asyncStorage.getStore();
  return logger.child({ correlationId: store?.correlationId });
}

// Usage in any handler or service
const log = getLogger();
log.info({ event: "payment_processed", amount: 50 });
// Output includes correlationId automatically
Log Levels Guide
TRACE: Extremely detailed (loop iterations, variable values)  -- dev only
DEBUG: Diagnostic info (function entry/exit, state changes)   -- dev/staging
INFO:  Normal operations (request handled, job completed)     -- all envs
WARN:  Unexpected but recoverable (retry succeeded, fallback used)
ERROR: Operation failed (unhandled exception, service down)
FATAL: Application cannot continue (missing config, DB unreachable)

Production default: INFO
Never log: passwords, tokens, PII, credit cards, full request bodies

Error Boundaries and Graceful Degradation

React Error Boundary
class ErrorBoundary extends React.Component<
  { fallback: React.ReactNode; children: React.ReactNode },
  { hasError: boolean; error?: Error }
> {
  state = { hasError: false, error: undefined };
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }
  componentDidCatch(error: Error, info: React.ErrorInfo) {
    logger.error(
      { err: error, componentStack: info.componentStack },
      "React error boundary caught error",
    );
  }
  render() {
    return this.state.hasError ? this.props.fallback : this.props.children;
  }
}

// Usage: wrap sections independently
<ErrorBoundary fallback={<p>Dashboard unavailable</p>}>
  <Dashboard />
</ErrorBoundary>
<ErrorBoundary fallback={<p>Sidebar unavailable</p>}>
  <Sidebar />
</ErrorBoundary>
Service Degradation
// Graceful degradation: serve stale data when service is down
async function getProductRecommendations(userId: string) {
  try {
    return await recommendationService.get(userId);
  } catch (error) {
    logger.warn(
      { userId, err: error },
      "Recommendation service unavailable, using fallback",
    );
    return getCachedRecommendations(userId) || getDefaultRecommendations();
  }
}

Retry Patterns

Exponential Backoff
async function withRetry<T>(
  fn: () => Promise<T>,
  options: {
    maxRetries?: number;
    baseDelay?: number;
    maxDelay?: number;
    retryOn?: (error: Error) => boolean;
  } = {},
): Promise<T> {
  const {
    maxRetries = 3,
    baseDelay = 1000,
    maxDelay = 30000,
    retryOn,
  } = options;
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxRetries) throw error;
      if (retryOn && !retryOn(error as Error)) throw error;

      const delay = Math.min(
        baseDelay * 2 ** attempt + Math.random() * 1000,
        maxDelay,
      );
      logger.warn({ attempt: attempt + 1, delay }, "Retrying operation");
      await new Promise((r) => setTimeout(r, delay));
    }
  }
  throw new Error("Unreachable");
}

// Usage: retry only on transient errors
const data = await withRetry(
  async () => {
    const res = await fetch("https://api.example.com/data");
    if (res.status >= 500 || res.status === 429)
      throw new Error(`HTTP ${res.status}`);
    return res;
  },
  {
    retryOn: (err) =>
      err instanceof TypeError || err.message.startsWith("HTTP "),
  },
);
Circuit Breaker
class CircuitBreaker {
  private failures = 0;
  private lastFailure = 0;
  private state: "closed" | "open" | "half-open" = "closed";

  constructor(
    private threshold: number = 5,
    private resetTimeout: number = 60000,
  ) {}

  async execute<T>(fn: () => Promise<T>, fallback?: () => T): Promise<T> {
    if (this.state === "open") {
      if (Date.now() - this.lastFailure > this.resetTimeout) {
        this.state = "half-open";
      } else {
        if (fallback) return fallback();
        throw new Error("Circuit breaker is open");
      }
    }

    try {
      const result = await fn();
      this.failures = 0;
      this.state = "closed";
      return result;
    } catch (error) {
      this.failures++;
      this.lastFailure = Date.now();
      if (this.failures >= this.threshold) this.state = "open";
      if (fallback) return fallback();
      throw error;
    }
  }
}

// Usage: trips open after 5 failures, resets after 30s
const paymentCircuit = new CircuitBreaker(5, 30000);
const result = await paymentCircuit.execute(
  () => paymentService.charge(amount),
  () => ({ queued: true, message: "Payment will be processed shortly" }),
);

Error Tracking Integration

Sentry Setup
import * as Sentry from "@sentry/node";

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: process.env.NODE_ENV === "production" ? 0.1 : 1.0,
  beforeSend(event) {
    // Scrub sensitive data
    if (event.request?.headers) delete event.request.headers["authorization"];
    return event;
  },
});

Sentry.setUser({ id: user.id, email: user.email });
Sentry.captureException(error, {
  tags: { subsystem: "payment", provider: "stripe" },
  extra: { orderId, amount },
});

User-Facing vs Internal Errors

// Map internal errors to user-friendly messages
const USER_MESSAGES: Record<string, string> = {
  VALIDATION_ERROR: "Please check your input and try again.",
  NOT_FOUND: "The requested resource could not be found.",
  RATE_LIMITED: "Too many requests. Please wait a moment.",
  PAYMENT_FAILED: "Payment could not be processed. Please try another method.",
  INTERNAL_ERROR: "Something went wrong. Please try again later.",
};

function toUserResponse(error: AppError) {
  return {
    error: {
      code: error.code,
      message: USER_MESSAGES[error.code] || USER_MESSAGES["INTERNAL_ERROR"],
    },
  };
}

// WRONG: Exposing internal details to users
res.status(500).json({
  error: 'QueryFailedError: relation "users" does not exist',
  stack: error.stack,
});

// CORRECT: Generic message to user, full details in logs
logger.error({ err: error, query }, "Database query failed");
res.status(500).json(toUserResponse(new AppError("DB error", 500)));

Common Anti-Patterns Summary

AVOID                              DO INSTEAD
-------------------------------------------------------------------
Empty catch blocks                 Log and handle or re-throw
Bare `except:` in Python           Catch specific exceptions
console.log for production         Structured logger (pino, winston)
Logging passwords/tokens           Redact sensitive fields
Retry without backoff              Exponential backoff with jitter
Retry on all errors                Only retry transient/network errors
No circuit breaker                 Circuit breaker for external calls
Exposing stack traces to users     Generic user messages, detailed logs
No correlation IDs                 Propagate correlation ID across services
One giant try/catch                Granular error handling per operation
Logging inside tight loops         Log summaries/aggregates
No error boundaries in React       Wrap independent sections separately
1---
2name: error-handling
3description: Implements error handling patterns, structured logging, retry strategies, circuit breakers, and graceful degradation. Use when designing error handling, setting up logging, implementing retries, adding error tracking, or when asked about error boundaries, log aggregation, alerting, or resilience patterns.
4---
5 
6# Error Handling & Observability
7 
8### When to Load
9 
10- **Trigger**: Try/catch patterns, retry logic, error responses, circuit breakers, structured logging
11- **Skip**: No error handling or observability involved in the current task
12 
13## Error Handling Workflow
14 
15Copy this checklist and track progress:
16 
17```
18Error Handling Progress:
19- [ ] Step 1: Define error taxonomy (categories and severity)
20- [ ] Step 2: Implement error handling by layer
21- [ ] Step 3: Set up structured logging
22- [ ] Step 4: Add retry and circuit breaker patterns
23- [ ] Step 5: Configure error tracking service
24- [ ] Step 6: Define user-facing error messages
25- [ ] Step 7: Validate against anti-patterns checklist
26```
27 
28## Error Handling Patterns by Language
29 
30### JavaScript / TypeScript
31 
32```typescript
33// Custom error hierarchy
34class AppError extends Error {
35 constructor(
36 message: string,
37 public statusCode: number = 500,
38 public code: string = "INTERNAL_ERROR",
39 public isOperational: boolean = true,
40 ) {
41 super(message);
42 this.name = this.constructor.name;
43 }
44}
45class NotFoundError extends AppError {
46 constructor(resource: string, id: string) {
47 super(`${resource} with id ${id} not found`, 404, "NOT_FOUND");
48 }
49}
50class ValidationError extends AppError {
51 constructor(public errors: Record<string, string[]>) {
52 super("Validation failed", 400, "VALIDATION_ERROR");
53 }
54}
55 
56// WRONG: Swallowing errors silently
57try {
58 await saveUser(data);
59} catch (e) {
60 // nothing here -- bug hides forever
61}
62 
63// WRONG: Catching and re-throwing without context
64try {
65 await saveUser(data);
66} catch (e) {
67 throw e; // pointless try/catch
68}
69 
70// CORRECT: Add context, handle or propagate
71try {
72 await saveUser(data);
73} catch (error) {
74 if (error instanceof ValidationError) {
75 return res.status(400).json({ errors: error.errors });
76 }
77 logger.error({ err: error, userId: data.id }, "Failed to save user");
78 throw new AppError("Unable to save user", 500, "USER_SAVE_FAILED");
79}
80```
81 
82### Express Global Error Handler
83 
84```typescript
85// Centralized error handler middleware (must have 4 params)
86app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
87 if (err instanceof AppError) {
88 logger.warn(
89 { code: err.code, statusCode: err.statusCode, path: req.path },
90 "Operational error",
91 );
92 return res.status(err.statusCode).json({
93 error: { code: err.code, message: err.message },
94 });
95 }
96 
97 // Unexpected errors -- these are bugs
98 logger.error({ err, path: req.path }, "Unexpected error");
99 res.status(500).json({
100 error: { code: "INTERNAL_ERROR", message: "An unexpected error occurred" },
101 });
102});
103```
104 
105### Python
106 
107```python
108# Custom exception hierarchy
109class AppError(Exception):
110 def __init__(self, message: str, code: str = "INTERNAL_ERROR", status: int = 500):
111 self.message = message
112 self.code = code
113 self.status = status
114 super().__init__(message)
115 
116class NotFoundError(AppError):
117 def __init__(self, resource: str, id: str):
118 super().__init__(f"{resource} {id} not found", "NOT_FOUND", 404)
119 
120class ValidationError(AppError):
121 def __init__(self, errors: dict[str, list[str]]):
122 self.errors = errors
123 super().__init__("Validation failed", "VALIDATION_ERROR", 400)
124 
125# WRONG: Bare except
126try:
127 result = process(data)
128except: # catches SystemExit, KeyboardInterrupt too!
129 pass
130 
131# CORRECT: Specific exceptions, proper logging
132try:
133 result = process(data)
134except ValidationError as e:
135 logger.warning("Validation failed", extra={"errors": e.errors})
136 raise
137except DatabaseError as e:
138 logger.error("Database error during processing", exc_info=True)
139 raise AppError("Processing failed", "PROCESS_FAILED") from e
140```
141 
142### Go
143 
144```go
145// Define sentinel errors and custom types
146var (
147 ErrNotFound = errors.New("resource not found")
148 ErrUnauthorized = errors.New("unauthorized")
149)
150 
151type ValidationError struct {
152 Field string
153 Message string
154}
155 
156func (e *ValidationError) Error() string {
157 return fmt.Sprintf("validation: %s - %s", e.Field, e.Message)
158}
159 
160// WRONG: Ignoring errors
161data, _ := json.Marshal(user) // error silently dropped
162 
163// WRONG: Only returning error string
164if err != nil {
165 return fmt.Errorf("failed: %s", err.Error()) // loses error chain
166}
167 
168// CORRECT: Wrap errors with context
169if err != nil {
170 return fmt.Errorf("saving user %s: %w", user.ID, err) // %w preserves chain
171}
172 
173// CORRECT: Check error types
174if errors.Is(err, ErrNotFound) {
175 http.Error(w, "Not found", http.StatusNotFound)
176 return
177}
178var valErr *ValidationError
179if errors.As(err, &valErr) {
180 http.Error(w, valErr.Error(), http.StatusBadRequest)
181 return
182}
183```
184 
185## Structured Logging
186 
187### JSON Log Format
188 
189```typescript
190// WRONG: Unstructured string logs
191console.log(`User ${userId} created order ${orderId} at ${new Date()}`);
192// Impossible to parse, filter, or aggregate
193 
194// CORRECT: Structured JSON logs
195import pino from "pino";
196 
197const logger = pino({
198 level: process.env.LOG_LEVEL || "info",
199 formatters: {
200 level: (label) => ({ level: label }),
201 },
202 redact: ["req.headers.authorization", "password", "ssn"],
203});
204logger.info({
205 event: "order_created",
206 userId: "123",
207 orderId: "456",
208 amount: 99.99,
209 currency: "USD",
210});
211// Output: {"level":"info","event":"order_created","userId":"123","orderId":"456",...}
212```
213 
214### Correlation IDs
215 
216```typescript
217// Middleware to propagate correlation ID across requests
218import { randomUUID } from "crypto";
219import { AsyncLocalStorage } from "async_hooks";
220 
221const asyncStorage = new AsyncLocalStorage<{ correlationId: string }>();
222 
223app.use((req, res, next) => {
224 const correlationId =
225 (req.headers["x-correlation-id"] as string) || randomUUID();
226 res.setHeader("x-correlation-id", correlationId);
227 
228 asyncStorage.run({ correlationId }, () => next());
229});
230 
231// Logger automatically includes correlation ID
232function getLogger() {
233 const store = asyncStorage.getStore();
234 return logger.child({ correlationId: store?.correlationId });
235}
236 
237// Usage in any handler or service
238const log = getLogger();
239log.info({ event: "payment_processed", amount: 50 });
240// Output includes correlationId automatically
241```
242 
243### Log Levels Guide
244 
245```
246TRACE: Extremely detailed (loop iterations, variable values) -- dev only
247DEBUG: Diagnostic info (function entry/exit, state changes) -- dev/staging
248INFO: Normal operations (request handled, job completed) -- all envs
249WARN: Unexpected but recoverable (retry succeeded, fallback used)
250ERROR: Operation failed (unhandled exception, service down)
251FATAL: Application cannot continue (missing config, DB unreachable)
252 
253Production default: INFO
254Never log: passwords, tokens, PII, credit cards, full request bodies
255```
256 
257## Error Boundaries and Graceful Degradation
258 
259### React Error Boundary
260 
261```tsx
262class ErrorBoundary extends React.Component<
263 { fallback: React.ReactNode; children: React.ReactNode },
264 { hasError: boolean; error?: Error }
265> {
266 state = { hasError: false, error: undefined };
267 static getDerivedStateFromError(error: Error) {
268 return { hasError: true, error };
269 }
270 componentDidCatch(error: Error, info: React.ErrorInfo) {
271 logger.error(
272 { err: error, componentStack: info.componentStack },
273 "React error boundary caught error",
274 );
275 }
276 render() {
277 return this.state.hasError ? this.props.fallback : this.props.children;
278 }
279}
280 
281// Usage: wrap sections independently
282<ErrorBoundary fallback={<p>Dashboard unavailable</p>}>
283 <Dashboard />
284</ErrorBoundary>
285<ErrorBoundary fallback={<p>Sidebar unavailable</p>}>
286 <Sidebar />
287</ErrorBoundary>
288```
289 
290### Service Degradation
291 
292```typescript
293// Graceful degradation: serve stale data when service is down
294async function getProductRecommendations(userId: string) {
295 try {
296 return await recommendationService.get(userId);
297 } catch (error) {
298 logger.warn(
299 { userId, err: error },
300 "Recommendation service unavailable, using fallback",
301 );
302 return getCachedRecommendations(userId) || getDefaultRecommendations();
303 }
304}
305```
306 
307## Retry Patterns
308 
309### Exponential Backoff
310 
311```typescript
312async function withRetry<T>(
313 fn: () => Promise<T>,
314 options: {
315 maxRetries?: number;
316 baseDelay?: number;
317 maxDelay?: number;
318 retryOn?: (error: Error) => boolean;
319 } = {},
320): Promise<T> {
321 const {
322 maxRetries = 3,
323 baseDelay = 1000,
324 maxDelay = 30000,
325 retryOn,
326 } = options;
327 for (let attempt = 0; attempt <= maxRetries; attempt++) {
328 try {
329 return await fn();
330 } catch (error) {
331 if (attempt === maxRetries) throw error;
332 if (retryOn && !retryOn(error as Error)) throw error;
333 
334 const delay = Math.min(
335 baseDelay * 2 ** attempt + Math.random() * 1000,
336 maxDelay,
337 );
338 logger.warn({ attempt: attempt + 1, delay }, "Retrying operation");
339 await new Promise((r) => setTimeout(r, delay));
340 }
341 }
342 throw new Error("Unreachable");
343}
344 
345// Usage: retry only on transient errors
346const data = await withRetry(
347 async () => {
348 const res = await fetch("https://api.example.com/data");
349 if (res.status >= 500 || res.status === 429)
350 throw new Error(`HTTP ${res.status}`);
351 return res;
352 },
353 {
354 retryOn: (err) =>
355 err instanceof TypeError || err.message.startsWith("HTTP "),
356 },
357);
358```
359 
360### Circuit Breaker
361 
362```typescript
363class CircuitBreaker {
364 private failures = 0;
365 private lastFailure = 0;
366 private state: "closed" | "open" | "half-open" = "closed";
367 
368 constructor(
369 private threshold: number = 5,
370 private resetTimeout: number = 60000,
371 ) {}
372 
373 async execute<T>(fn: () => Promise<T>, fallback?: () => T): Promise<T> {
374 if (this.state === "open") {
375 if (Date.now() - this.lastFailure > this.resetTimeout) {
376 this.state = "half-open";
377 } else {
378 if (fallback) return fallback();
379 throw new Error("Circuit breaker is open");
380 }
381 }
382 
383 try {
384 const result = await fn();
385 this.failures = 0;
386 this.state = "closed";
387 return result;
388 } catch (error) {
389 this.failures++;
390 this.lastFailure = Date.now();
391 if (this.failures >= this.threshold) this.state = "open";
392 if (fallback) return fallback();
393 throw error;
394 }
395 }
396}
397 
398// Usage: trips open after 5 failures, resets after 30s
399const paymentCircuit = new CircuitBreaker(5, 30000);
400const result = await paymentCircuit.execute(
401 () => paymentService.charge(amount),
402 () => ({ queued: true, message: "Payment will be processed shortly" }),
403);
404```
405 
406## Error Tracking Integration
407 
408### Sentry Setup
409 
410```typescript
411import * as Sentry from "@sentry/node";
412 
413Sentry.init({
414 dsn: process.env.SENTRY_DSN,
415 environment: process.env.NODE_ENV,
416 tracesSampleRate: process.env.NODE_ENV === "production" ? 0.1 : 1.0,
417 beforeSend(event) {
418 // Scrub sensitive data
419 if (event.request?.headers) delete event.request.headers["authorization"];
420 return event;
421 },
422});
423 
424Sentry.setUser({ id: user.id, email: user.email });
425Sentry.captureException(error, {
426 tags: { subsystem: "payment", provider: "stripe" },
427 extra: { orderId, amount },
428});
429```
430 
431## User-Facing vs Internal Errors
432 
433```typescript
434// Map internal errors to user-friendly messages
435const USER_MESSAGES: Record<string, string> = {
436 VALIDATION_ERROR: "Please check your input and try again.",
437 NOT_FOUND: "The requested resource could not be found.",
438 RATE_LIMITED: "Too many requests. Please wait a moment.",
439 PAYMENT_FAILED: "Payment could not be processed. Please try another method.",
440 INTERNAL_ERROR: "Something went wrong. Please try again later.",
441};
442 
443function toUserResponse(error: AppError) {
444 return {
445 error: {
446 code: error.code,
447 message: USER_MESSAGES[error.code] || USER_MESSAGES["INTERNAL_ERROR"],
448 },
449 };
450}
451 
452// WRONG: Exposing internal details to users
453res.status(500).json({
454 error: 'QueryFailedError: relation "users" does not exist',
455 stack: error.stack,
456});
457 
458// CORRECT: Generic message to user, full details in logs
459logger.error({ err: error, query }, "Database query failed");
460res.status(500).json(toUserResponse(new AppError("DB error", 500)));
461```
462 
463## Common Anti-Patterns Summary
464 
465```
466AVOID DO INSTEAD
467-------------------------------------------------------------------
468Empty catch blocks Log and handle or re-throw
469Bare `except:` in Python Catch specific exceptions
470console.log for production Structured logger (pino, winston)
471Logging passwords/tokens Redact sensitive fields
472Retry without backoff Exponential backoff with jitter
473Retry on all errors Only retry transient/network errors
474No circuit breaker Circuit breaker for external calls
475Exposing stack traces to users Generic user messages, detailed logs
476No correlation IDs Propagate correlation ID across services
477One giant try/catch Granular error handling per operation
478Logging inside tight loops Log summaries/aggregates
479No error boundaries in React Wrap independent sections separately
480```
481 

Discussion

Alternatives

Skill CreatorCreate new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.Coding · Apache-2.0Professional Full-Stack Developer for Network Mapping & Monitoring ApplicationAct as a professional full-stack developer tasked with building a web application for mapping and monitoring networks using Mikrotik Netwatch API. Implement multi-user role-based management to handle devices, monitor their status, and manage user subscriptions.Coding · CC0-1.0Prompt refinerHigh-end Prompt Engineering & Prompt Refiner skill. Transforms raw or messy user requests into concise, token-efficient, high-performance master prompts for systems like GPT, Claude, and Gemini. Use when you want to optimize or redesign a prompt so it solves the problem reliably while minimizing tokens.Data & AI · CC0-1.0Constraint driven developmentEstablishes a project's quality bar as a written contract and stops agents quietly lowering it. Interviews the user on which dimensions matter, supplies sane default thresholds when they have no number in mind, records everything in CONSTRAINTS.md, and watches the diff for a weakened bar — new @ts-ignore or eslint-disable suppressions, skipped or deleted tests, assertions stripped out, unimplemented stubs, thresholds edited down. Use when no quality bar is written down, when the user says "set up constraints" or "define our standards", when the user wants dimensions they care about — accessibility, web performance, coverage — set up as enforced constraints, when an agent keeps silencing checks or skipping tests to get to green, when you need a coverage or performance threshold and don't know what number to pick, or when an agent writes more code than anyone will read.Coding · MIT