effect-errors-retries

mepuka's avatarfrom mepuka

Error modeling and recovery with TaggedError, catchTag(s), mapError, and retry schedules. Use when defining errors or adding resilience.

⭐0stars🔀0forks📁View on GitHub🕐Updated Dec 25, 2025⬇️2downloads

When & Why to Use This Skill

This Claude skill facilitates robust error modeling and system resilience using the Effect library in TypeScript. It empowers developers to move beyond generic error handling by implementing type-safe domain errors with TaggedError, designing sophisticated retry strategies with exponential backoff and jitter, and managing execution boundaries through precise error mapping and timeouts. By following these patterns, developers can build highly predictable, fault-tolerant applications that handle failures as first-class citizens.

Use Cases

  • Domain Error Modeling: Creating type-safe, structured error classes using TaggedError to replace generic 'Error' types, ensuring the compiler tracks all possible failure modes.
  • Resilient Network Requests: Configuring advanced retry schedules with exponential backoff, jitter, and maximum recursion limits to gracefully handle transient API or database failures.
  • Error Boundary Mapping: Transforming low-level infrastructure errors (e.g., HTTP 404s or SQL connection errors) into meaningful domain-specific errors at the service layer to maintain clean architecture.
  • Latency & Resource Control: Implementing execution timeouts and race conditions to prevent hanging operations and ensure system responsiveness under heavy load.
  • Selective Recovery: Using catchTags and catchIf to precisely handle specific failure cases while allowing unexpected defects to fail fast and safely.
nameeffect-errors-retries
descriptionError modeling and recovery with TaggedError, catchTag(s), mapError, and retry schedules. Use when defining errors or adding resilience.
allowed-toolsRead, Grep, Glob, Edit, Write, mcp__effect-docs__effect_docs_search

Errors & Retries

When to use

  • You’re defining domain errors and recovery policies
  • You need to map infrastructure errors to domain boundaries
  • You must add retries, backoff, jitter, and timeouts

Model Errors

import { Data } from "effect"

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

Map/Boundary

const repoCall = Effect.fail(new ValidationError({ field: "name", reason: "empty" }))

const service = repoCall.pipe(
  Effect.mapError((e) => new ServiceError({ cause: e }))
)

Recover Precisely

program.pipe(
  Effect.catchTags({
    ValidationError: (e) => Effect.succeed(fix(e)),
    NotFoundError: (e) => Effect.succeed(defaultValue)
  })
)

Retry Policies

import { Schedule } from "effect"

const retry = Schedule.exponential("100 millis").pipe(
  Schedule.jittered,
  Schedule.compose(Schedule.recurs(5))
)

const resilient = program.pipe(Effect.retry(retry))

Timeouts & Races

const withTimeout = Effect.timeout(program, "2 seconds")

Guidance & Pitfalls

  • Create separate TaggedError types per failure mode; avoid generic Error
  • Map low-level (HTTP/DB) errors to domain errors at the boundary
  • Retry only transient errors (use predicates with Schedule.whileInput)
  • Always combine retry with timeouts for bounded latency
  • Prefer catchTags over broad catchAll for clarity and safety

Real-world snippet: Map HTTP errors and retry selectively

// Verify URL with HEAD-range request, map ResponseError to domain errors,
// retry with exponential backoff unless invalid.
const verify = http.get(url, { headers: { range: "bytes=0-0" } }).pipe(
  Effect.flatMap(HttpClientResponse.filterStatus((s) => s < 400)),
  Effect.catchIf(
    (e) => e._tag === "ResponseError",
    (cause) =>
      cause.response.status < 500
        ? Effect.fail(new VideoInvalidError({ cause: "NotFound" }))
        : Effect.fail(new ExternalLoomError({ cause: cause.response }))
  ),
  Effect.retry({
    schedule: Schedule.exponential("200 millis"),
    times: 3,
    while: (e) => e._tag !== "VideoInvalidError"
  }),
  Effect.catchTag("RequestError", Effect.die)
)

Real-world snippet: Capture structured errors from Cause

import { Cause } from "effect"

const captureErrors = (cause: Cause.Cause<unknown>) => Effect.gen(function* () {
  if (Cause.isInterruptedOnly(cause)) return { interrupted: true, errors: [] }
  const raw = captureErrorsFrom(cause)
  const errors = yield* Effect.forEach(raw, transformRawError({ stripCwd: true }))
  return { interrupted: false, errors }
})

Quick Heuristics

  • Domain errors → TaggedError per type
  • Map low-level to high-level at boundaries
  • Retry only transient errors; predicate with Schedule.whileInput

Local Source Reference

CRITICAL: Search local Effect source before implementing

The full Effect source code is available at docs/effect-source/. Always search the actual implementation before writing Effect code.

Key Source Files

  • Effect: docs/effect-source/effect/src/Effect.ts
  • Data: docs/effect-source/effect/src/Data.ts
  • Schedule: docs/effect-source/effect/src/Schedule.ts
  • Cause: docs/effect-source/effect/src/Cause.ts

Example Searches

# Find error handling patterns
grep -F "catchTag" docs/effect-source/effect/src/Effect.ts
grep -F "catchAll" docs/effect-source/effect/src/Effect.ts
grep -F "mapError" docs/effect-source/effect/src/Effect.ts

# Study TaggedError
grep -F "TaggedError" docs/effect-source/effect/src/Data.ts

# Find retry and schedule patterns
grep -F "retry" docs/effect-source/effect/src/Schedule.ts
grep -F "exponential" docs/effect-source/effect/src/Schedule.ts
grep -F "jittered" docs/effect-source/effect/src/Schedule.ts

# Study Cause operations
grep -F "isInterruptedOnly" docs/effect-source/effect/src/Cause.ts

Workflow

  1. Identify the error handling API you need (e.g., catchTag, retry)
  2. Search docs/effect-source/effect/src/Effect.ts for the implementation
  3. Study the types and error recovery patterns
  4. Look at test files for usage examples
  5. Write your code based on real implementations

Real source code > documentation > assumptions

References