agentsclimarketplace

Error handling patterns

Skill autohandai/community-skills/error-handling-patterns

A collection of curated, useful, and safe skills for Autohand Code CLI Agent

Install
npx -y skills add autohandai/community-skills --skill error-handling-patterns

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 9 stars9 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Robust error handling patterns for TypeScript applications

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

6.7 KB, as published. Nobody here has run it

Error Handling Patterns

Core Principles

  1. Fail fast - Detect and report errors early
  2. Fail safely - Errors shouldn't crash the application
  3. Fail informatively - Provide actionable error messages
  4. Recover gracefully - Handle expected failures

Custom Error Classes

// Base application error
export class AppError extends Error {
  constructor(
    message: string,
    public readonly code: string,
    public readonly statusCode: number = 500,
    public readonly details?: unknown
  ) {
    super(message);
    this.name = this.constructor.name;
    Error.captureStackTrace(this, this.constructor);
  }

  toJSON() {
    return {
      code: this.code,
      message: this.message,
      ...(this.details && { details: this.details }),
    };
  }
}

// Specific error types
export class ValidationError extends AppError {
  constructor(message: string, details?: Record<string, string>) {
    super(message, 'VALIDATION_ERROR', 400, details);
  }
}

export class NotFoundError extends AppError {
  constructor(resource: string, id: string) {
    super(`${resource} with id '${id}' not found`, 'NOT_FOUND', 404);
  }
}

export class UnauthorizedError extends AppError {
  constructor(message = 'Authentication required') {
    super(message, 'UNAUTHORIZED', 401);
  }
}

export class ForbiddenError extends AppError {
  constructor(message = 'Insufficient permissions') {
    super(message, 'FORBIDDEN', 403);
  }
}

Result Type Pattern

type Result<T, E = Error> =
  | { success: true; data: T }
  | { success: false; error: E };

// Helper functions
function ok<T>(data: T): Result<T, never> {
  return { success: true, data };
}

function err<E>(error: E): Result<never, E> {
  return { success: false, error };
}

// Usage
async function findUser(id: string): Promise<Result<User, NotFoundError>> {
  const user = await db.users.findUnique({ where: { id } });
  if (!user) {
    return err(new NotFoundError('User', id));
  }
  return ok(user);
}

// Consuming
const result = await findUser('123');
if (!result.success) {
  console.error(result.error.message);
  return;
}
const user = result.data; // Type-safe User

Error Boundaries (React)

import { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
  onError?: (error: Error, info: ErrorInfo) => void;
}

interface State {
  hasError: boolean;
  error: Error | null;
}

export class ErrorBoundary extends Component<Props, State> {
  state: State = { hasError: false, error: null };

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, info: ErrorInfo) {
    this.props.onError?.(error, info);
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback ?? (
        <div role="alert">
          <h2>Something went wrong</h2>
          <pre>{this.state.error?.message}</pre>
        </div>
      );
    }
    return this.props.children;
  }
}

Async Error Handling

// Wrapper for async functions
function tryCatch<T>(
  promise: Promise<T>
): Promise<[null, T] | [Error, null]> {
  return promise
    .then((data) => [null, data] as [null, T])
    .catch((error) => [error as Error, null]);
}

// Usage
const [error, user] = await tryCatch(fetchUser(id));
if (error) {
  handleError(error);
  return;
}
// user is guaranteed to be defined here

// Multiple operations
async function processOrder(orderId: string) {
  const [orderError, order] = await tryCatch(getOrder(orderId));
  if (orderError) return err(orderError);

  const [paymentError] = await tryCatch(processPayment(order));
  if (paymentError) {
    await tryCatch(rollbackOrder(order));
    return err(paymentError);
  }

  return ok(order);
}

Retry Pattern

interface RetryOptions {
  maxAttempts: number;
  delayMs: number;
  backoff?: 'linear' | 'exponential';
  shouldRetry?: (error: Error) => boolean;
}

async function withRetry<T>(
  fn: () => Promise<T>,
  options: RetryOptions
): Promise<T> {
  const { maxAttempts, delayMs, backoff = 'exponential', shouldRetry } = options;
  let lastError: Error;

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;

      if (shouldRetry && !shouldRetry(lastError)) {
        throw lastError;
      }

      if (attempt < maxAttempts) {
        const delay = backoff === 'exponential'
          ? delayMs * Math.pow(2, attempt - 1)
          : delayMs * attempt;
        await sleep(delay);
      }
    }
  }

  throw lastError!;
}

// Usage
const data = await withRetry(() => fetchFromAPI(), {
  maxAttempts: 3,
  delayMs: 1000,
  shouldRetry: (err) => err.message.includes('timeout'),
});

Validation Errors

import { z } from 'zod';

function validateInput<T>(schema: z.ZodSchema<T>, input: unknown): Result<T, ValidationError> {
  const result = schema.safeParse(input);

  if (!result.success) {
    const details = result.error.flatten().fieldErrors;
    return err(new ValidationError('Invalid input', details));
  }

  return ok(result.data);
}

// Usage
const userSchema = z.object({
  email: z.string().email(),
  age: z.number().min(18),
});

const result = validateInput(userSchema, req.body);
if (!result.success) {
  return res.status(400).json(result.error.toJSON());
}

Logging Errors

interface ErrorContext {
  userId?: string;
  requestId?: string;
  path?: string;
  [key: string]: unknown;
}

function logError(error: Error, context: ErrorContext = {}) {
  const payload = {
    timestamp: new Date().toISOString(),
    name: error.name,
    message: error.message,
    stack: error.stack,
    ...(error instanceof AppError && { code: error.code }),
    ...context,
  };

  console.error(JSON.stringify(payload));

  // Send to error tracking service
  if (process.env.NODE_ENV === 'production') {
    // Sentry.captureException(error, { extra: context });
  }
}

Best Practices

  1. Create specific error types for different failure modes
  2. Use Result types for expected failures (validation, not found)
  3. Throw errors for unexpected failures (bugs, system errors)
  4. Include context in error messages for debugging
  5. Log at boundaries - API handlers, event processors
  6. Validate at boundaries - external input, API responses
  7. Implement retry for transient failures
  8. Use error boundaries in React for graceful degradation

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.