EF

effect-patterns-error-handling

Provides patterns for managing error handling and validation in Effect-TS applications.

Install

mkdir -p .claude/skills/effect-patterns-error-handling && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/3129" && unzip -o skill.zip -d .claude/skills/effect-patterns-error-handling && rm skill.zip

Installs to .claude/skills/effect-patterns-error-handling

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Effect-TS patterns for Error Handling. Use when working with error handling in Effect-TS applications.
102 chars✓ has a “when” trigger
Advanced

Key capabilities

  • Implement error accumulation logic
  • Manage batch operation validation failures
  • Structure type-safe validation flows
  • Standardize error reporting for forms
  • Refactor early-exit errors into collections

How it works

Applies established Effect-TS error-handling idioms, replacing individual error throwing with structured collection patterns.

Inputs & outputs

You give it
Validation logic or batch operation code
You get back
Refactored code using accumulation patterns

When to use effect-patterns-error-handling

  • Implementing error accumulation for forms
  • Refactoring error handling logic
  • Building type-safe validation flows
  • Managing batch operation errors

About this skill

Effect-TS Patterns: Error Handling

This skill provides 3 curated Effect-TS patterns for error handling. Use this skill when working on tasks related to:

  • error handling
  • Best practices in Effect-TS applications
  • Real-world patterns and solutions

🟡 Intermediate Patterns

Error Handling Pattern 1: Accumulating Multiple Errors

Rule: Use error accumulation to report all problems at once rather than failing early, critical for validation and batch operations.

Good Example:

This example demonstrates error accumulation patterns.

import { Effect, Data, Cause } from "effect";

interface ValidationError {
  field: string;
  message: string;
  value?: unknown;
}

interface ProcessingResult<T> {
  successes: T[];
  errors: ValidationError[];
}

// Example 1: Form validation with error accumulation
const program = Effect.gen(function* () {
  console.log(`\n[ERROR ACCUMULATION] Collecting multiple errors\n`);

  // Form data
  interface FormData {
    name: string;
    email: string;
    age: number;
    phone: string;
  }

  const validateForm = (data: FormData): ValidationError[] => {
    const errors: ValidationError[] = [];

    // Validation 1: Name
    if (!data.name || data.name.trim().length === 0) {
      errors.push({
        field: "name",
        message: "Name is required",
        value: data.name,
      });
    } else if (data.name.length < 2) {
      errors.push({
        field: "name",
        message: "Name must be at least 2 characters",
        value: data.name,
      });
    }

    // Validation 2: Email
    if (!data.email) {
      errors.push({
        field: "email",
        message: "Email is required",
        value: data.email,
      });
    } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
      errors.push({
        field: "email",
        message: "Email format invalid",
        value: data.email,
      });
    }

    // Validation 3: Age
    if (data.age < 0 || data.age > 150) {
      errors.push({
        field: "age",
        message: "Age must be between 0 and 150",
        value: data.age,
      });
    }

    // Validation 4: Phone
    if (data.phone && !/^\d{3}-\d{3}-\d{4}$/.test(data.phone)) {
      errors.push({
        field: "phone",
        message: "Phone must be in format XXX-XXX-XXXX",
        value: data.phone,
      });
    }

    return errors;
  };

  // Example 1: Form with multiple errors
  console.log(`[1] Form validation with multiple errors:\n`);

  const invalidForm: FormData = {
    name: "",
    email: "not-an-email",
    age: 200,
    phone: "invalid",
  };

  const validationErrors = validateForm(invalidForm);

  yield* Effect.log(`[VALIDATION] Found ${validationErrors.length} errors:\n`);

  for (const error of validationErrors) {
    yield* Effect.log(`  ✗ ${error.field}: ${error.message}`);
  }

  // Example 2: Batch processing with partial success
  console.log(`\n[2] Batch processing (accumulate successes and failures):\n`);

  interface Record {
    id: string;
    data: string;
  }

  const processRecord = (record: Record): Result<string> => {
    if (record.id.length === 0) {
      return { success: false, error: "Missing ID" };
    }

    if (record.data.includes("ERROR")) {
      return { success: false, error: "Invalid data" };
    }

    return { success: true, value: `processed-${record.id}` };
  };

  interface Result<T> {
    success: boolean;
    value?: T;
    error?: string;
  }

  const records: Record[] = [
    { id: "rec1", data: "ok" },
    { id: "", data: "ok" }, // Error: missing ID
    { id: "rec3", data: "ok" },
    { id: "rec4", data: "ERROR" }, // Error: invalid data
    { id: "rec5", data: "ok" },
  ];

  const results: ProcessingResult<string> = {
    successes: [],
    errors: [],
  };

  for (const record of records) {
    const result = processRecord(record);

    if (result.success) {
      results.successes.push(result.value!);
    } else {
      results.errors.push({
        field: record.id || "unknown",
        message: result.error!,
      });
    }
  }

  yield* Effect.log(
    `[BATCH] Processed ${records.length} records`
  );
  yield* Effect.log(`[BATCH] ✓ ${results.successes.length} succeeded`);
  yield* Effect.log(`[BATCH] ✗ ${results.errors.length} failed\n`);

  for (const success of results.successes) {
    yield* Effect.log(`  ✓ ${success}`);
  }

  for (const error of results.errors) {
    yield* Effect.log(`  ✗ [${error.field}] ${error.message}`);
  }

  // Example 3: Multi-step validation with error accumulation
  console.log(`\n[3] Multi-step validation (all checks run):\n`);

  interface ServiceHealth {
    diskSpace: boolean;
    memory: boolean;
    network: boolean;
    database: boolean;
  }

  const diagnostics: ValidationError[] = [];

  // Check 1: Disk space
  const diskFree = 50; // MB

  if (diskFree < 100) {
    diagnostics.push({
      field: "disk-space",
      message: `Only ${diskFree}MB free (need 100MB)`,
      value: diskFree,
    });
  }

  // Check 2: Memory
  const memUsage = 95; // percent

  if (memUsage > 85) {
    diagnostics.push({
      field: "memory",
      message: `Using ${memUsage}% (threshold: 85%)`,
      value: memUsage,
    });
  }

  // Check 3: Network
  const latency = 500; // ms

  if (latency > 200) {
    diagnostics.push({
      field: "network",
      message: `Latency ${latency}ms (threshold: 200ms)`,
      value: latency,
    });
  }

  // Check 4: Database
  const dbConnections = 95;
  const dbMax = 100;

  if (dbConnections > dbMax * 0.8) {
    diagnostics.push({
      field: "database",
      message: `${dbConnections}/${dbMax} connections (80% threshold)`,
      value: dbConnections,
    });
  }

  if (diagnostics.length === 0) {
    yield* Effect.log(`[HEALTH] ✓ All systems normal\n`);
  } else {
    yield* Effect.log(
      `[HEALTH] ✗ ${diagnostics.length} issue(s) detected:\n`
    );

    for (const diag of diagnostics) {
      yield* Effect.log(`  ⚠ ${diag.field}: ${diag.message}`);
    }
  }

  // Example 4: Error collection with retry decisions
  console.log(`\n[4] Error collection for retry strategy:\n`);

  interface ErrorWithContext {
    operation: string;
    error: string;
    retryable: boolean;
    timestamp: Date;
  }

  const operationErrors: ErrorWithContext[] = [];

  const operations = [
    { name: "fetch-config", fail: false },
    { name: "connect-db", fail: true },
    { name: "load-cache", fail: true },
    { name: "start-server", fail: false },
  ];

  for (const op of operations) {
    if (op.fail) {
      operationErrors.push({
        operation: op.name,
        error: "Operation failed",
        retryable: op.name !== "fetch-config",
        timestamp: new Date(),
      });
    }
  }

  yield* Effect.log(`[OPERATIONS] ${operationErrors.length} errors:\n`);

  for (const err of operationErrors) {
    const status = err.retryable ? "🔄 retryable" : "❌ non-retryable";
    yield* Effect.log(`  ${status}: ${err.operation}`);
  }

  if (operationErrors.every((e) => e.retryable)) {
    yield* Effect.log(`\n[DECISION] All errors retryable, will retry\n`);
  } else {
    yield* Effect.log(`\n[DECISION] Some non-retryable errors, manual intervention needed\n`);
  }
});

Effect.runPromise(program);

Rationale:

Error accumulation strategies:

  • Collect errors: Gather all failures before reporting
  • Fail late: Continue processing despite errors
  • Contextual errors: Keep error location/operation info
  • Error summary: Aggregate for reporting
  • Partial success: Return valid results + errors

Pattern: Use Cause aggregation, Result types, or custom error structures


Failing fast causes problems:

Problem 1: Form validation

  • User submits form with 10 field errors
  • Fail on first error: "Name required"
  • User fixes name, submits again
  • New error: "Email invalid"
  • User submits 10 times before fixing all errors
  • Frustration, reduced productivity

Problem 2: Batch processing

  • Process 1000 records, fail on record 5
  • 995 records not processed
  • User manually retries
  • Repeats for each error type
  • Inefficient

Problem 3: System diagnostics

  • Service health check fails
  • Report: "Check 1 failed"
  • Fix check 1, service still down
  • Hidden problem: checks 2, 3, and 4 also failed
  • Time wasted diagnosing

Solutions:

Error accumulation:

  • Run all validations
  • Collect errors
  • Report all problems
  • User fixes once, not 10 times

Partial success:

  • Process all records
  • Track successes and failures
  • Return: "950 succeeded, 50 failed"
  • No re-processing

Comprehensive diagnostics:

  • Run all checks
  • Report all failures
  • Quick root cause analysis
  • Faster resolution


🟠 Advanced Patterns

Error Handling Pattern 2: Error Propagation and Chains

Rule: Use error propagation to preserve context through effect chains, enabling debugging and recovery at the right abstraction level.

Good Example:

This example demonstrates error propagation with context.

import { Effect, Data, Cause } from "effect";

// Domain-specific errors with context
class DatabaseError extends Data.TaggedError("DatabaseError")<{
  query: string;
  parameters: unknown[];
  cause: Error;
}> {}

class NetworkError extends Data.TaggedError("NetworkError")<{
  endpoint: string;
  method: string;
  statusCode?: number;
  cause: Error;
}> {}

class ValidationError extends Data.TaggedError("ValidationError")<{
  field: string;
  value: unknown;
  reason: string;
}> {}

class BusinessLogicError extends Data.TaggedError("BusinessLogicError")<{
  operation: string;
  context: Record<string, unknown>;
  originalError: Error;
}> {}

const program = Effect.gen(function* () {
  console.log(`\n[ERROR PROPAGATION] Error chains with context\n`);

  // Example 1: Simple error propagation
  console.log(`[1] Error propagation through layers:\n`);

  const lowLevelOperation = Effect.gen(function* () {
    yield* Effect.log(`[LAYER 1] Low-level operatio

---

*Content truncated.*

When not to use it

  • Building simple applications without Effect-TS
  • Performance-critical tasks where error overhead matters

Prerequisites

Effect-TS

Limitations

  • Only for Effect-TS applications
  • Increases complexity for trivial operations

How it compares

It enforces batch error reporting instead of the default behavior of stopping at the first error.

Compared to similar skills

effect-patterns-error-handling side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
effect-patterns-error-handling (this skill)17moNo flagsAdvanced
effect-patterns-scheduling17moNo flagsBeginner
nextjs-server-side-error-debugging16moReviewIntermediate
typescript-node-esm-compiler-runtime04moNo flagsAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

effect-patterns-scheduling

PaulJPhilp

Effect-TS patterns for Scheduling. Use when working with scheduling in Effect-TS applications.

12

nextjs-server-side-error-debugging

blader

Debug getServerSideProps and getStaticProps errors in Next.js. Use when: (1) Page shows generic error but browser console is empty, (2) API routes return 500 with no details, (3) Server-side code fails silently, (4) Error only occurs on refresh not client navigation. Check terminal/server logs instead of browser for actual error messages.

11

typescript-node-esm-compiler-runtime

GonkaGate

Own TypeScript plus Node.js ESM compiler/runtime correctness. Use whenever the real question is why TypeScript compiles but Node fails, how `tsconfig`/`package.json`/entrypoint/runtime mode must align, whether relative imports should use `.js` or `.ts`, how `nodenext`/`node20`/`verbatimModuleSyntax`

00

cross-platform-paths

microsoft

Critical patterns for cross-platform path handling in this VS Code extension. Windows vs POSIX path bugs are the #1 source of issues. Use this skill when reviewing or writing path-related code.

00

supabase-developer

daffy0208

Build full-stack applications with Supabase (PostgreSQL, Auth, Storage, Real-time, Edge Functions). Use when implementing authentication, database design with RLS, file storage, real-time features, or serverless functions.

95185

payload

payloadcms

Use when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.

73206

Search skills

Search the agent skills registry