Claude
Skills
Sign in
Back

effect-error-handling

Included with Lifetime
$97 forever

Use when Effect error handling patterns including catchAll, catchTag, either, option, and typed errors. Use for handling expected errors in Effect applications.

General

What this skill does


# Effect Error Handling

Master type-safe error handling in Effect applications. This skill covers
expected errors, error recovery, selective error handling, and error
transformations using Effect's error management operators.

## Expected Errors vs Defects

Effect distinguishes between two types of failures:

- **Expected Errors (E channel)**: Recoverable errors tracked in the type system
- **Defects**: Unexpected failures (bugs, programming errors)

```typescript
import { Effect } from "effect"

// Expected error - tracked in type
interface ValidationError {
  _tag: "ValidationError"
  field: string
  message: string
}

const validateEmail = (email: string): Effect.Effect<string, ValidationError, never> => {
  if (!email.includes("@")) {
    return Effect.fail({
      _tag: "ValidationError",
      field: "email",
      message: "Invalid email format"
    })
  }
  return Effect.succeed(email)
}

// Defect - throws, becomes unexpected failure
const riskyOperation = Effect.sync(() => {
  throw new Error("Unexpected error") // This is a defect
})

// Proper way - expected error
const safeOperation = Effect.try({
  try: () => {
    // Code that might throw
    return riskyParse(data)
  },
  catch: (error) => ({
    _tag: "ParseError",
    message: String(error)
  })
})
```

## Tagged Error Types

Use tagged unions for error types to enable pattern matching:

```typescript
import { Effect } from "effect"

// Define tagged error types
interface NotFoundError {
  _tag: "NotFoundError"
  id: string
}

interface UnauthorizedError {
  _tag: "UnauthorizedError"
  userId: string
}

interface NetworkError {
  _tag: "NetworkError"
  message: string
}

type AppError = NotFoundError | UnauthorizedError | NetworkError

// Functions returning typed errors
const fetchUser = (id: string): Effect.Effect<User, NotFoundError | NetworkError, never> => {
  // Implementation
}

const authenticate = (token: string): Effect.Effect<User, UnauthorizedError | NetworkError, never> => {
  // Implementation
}
```

## Catching All Errors

### Effect.catchAll - Recover from Any Error

Catches all expected errors and provides fallback:

```typescript
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const user = yield* fetchUser("123")
  return user
}).pipe(
  Effect.catchAll((error) =>
    Effect.succeed({ id: "default", name: "Guest" })
  )
)
// Effect<User, never, never> - Error channel is now never

// With error inspection
const programWithLogging = Effect.gen(function* () {
  const user = yield* fetchUser("123")
  return user
}).pipe(
  Effect.catchAll((error) => {
    console.error("Error occurred:", error)
    return Effect.succeed(defaultUser)
  })
)

// Fallback to another effect
const programWithFallback = pipe(
  fetchUser("123"),
  Effect.catchAll(() => fetchUserFromCache("123"))
)
```

## Selective Error Handling

### Effect.catchTag - Handle Specific Error Types

Catches errors by their `_tag` field:

```typescript
import { Effect, pipe } from "effect"

const program = pipe(
  fetchUser("123"),
  Effect.catchTag("NotFoundError", (error) =>
    Effect.succeed({ id: error.id, name: "Not Found" })
  )
)
// Still can fail with NetworkError

// Handling multiple tags
const program2 = pipe(
  authenticatedRequest(),
  Effect.catchTag("UnauthorizedError", (error) =>
    Effect.fail({ _tag: "LoginRequired" })
  ),
  Effect.catchTag("NetworkError", (error) =>
    retryRequest()
  )
)

// Using Effect.gen with early return
const program3 = Effect.gen(function* () {
  const result = yield* riskyOperation().pipe(
    Effect.catchTag("TemporaryError", () =>
      Effect.succeed(null)
    )
  )
  return result
})
```

### Effect.catchTags - Handle Multiple Error Types

```typescript
import { Effect, pipe } from "effect"

const program = pipe(
  complexOperation(),
  Effect.catchTags({
    NotFoundError: (error) =>
      Effect.succeed(defaultValue),
    UnauthorizedError: (error) =>
      Effect.fail({ _tag: "LoginRequired" }),
    NetworkError: (error) =>
      retryOperation()
  })
)

// With different recovery strategies
const programWithStrategies = pipe(
  processPayment(amount),
  Effect.catchTags({
    InsufficientFunds: (error) =>
      Effect.fail({ _tag: "PaymentDeclined", reason: "insufficient-funds" }),
    NetworkError: () =>
      retryPayment(amount),
    ValidationError: (error) =>
      Effect.fail({ _tag: "InvalidPayment", field: error.field })
  })
)
```

### Effect.catchIf - Conditional Error Handling

Catches errors that match a predicate:

```typescript
import { Effect, pipe } from "effect"

const isRetryable = (error: AppError): boolean => {
  return error._tag === "NetworkError" || error._tag === "TimeoutError"
}

const program = pipe(
  fetchData(),
  Effect.catchIf(isRetryable, (error) =>
    retryFetchData()
  )
)

// With type narrowing
const program2 = pipe(
  operation(),
  Effect.catchIf(
    (error): error is NetworkError => error._tag === "NetworkError",
    (error) => {
      // TypeScript knows error is NetworkError here
      console.log("Network error:", error.message)
      return retry()
    }
  )
)
```

### Effect.catchSome - Partial Error Handling

Catches errors and optionally handles them:

```typescript
import { Effect, Option, pipe } from "effect"

const program = pipe(
  fetchUser("123"),
  Effect.catchSome((error) => {
    if (error._tag === "NotFoundError") {
      return Option.some(Effect.succeed(guestUser))
    }
    return Option.none() // Don't handle, propagate error
  })
)

// Complex decision logic
const programWithDecision = pipe(
  processRequest(request),
  Effect.catchSome((error) => {
    if (error._tag === "RateLimitError" && error.retryAfter < 1000) {
      return Option.some(
        Effect.sleep(error.retryAfter).pipe(
          Effect.andThen(processRequest(request))
        )
      )
    }
    return Option.none()
  })
)
```

## Converting Errors

### Effect.either - Convert to Either<Success, Error>

Transforms an effect into one that cannot fail, wrapping result in Either:

```typescript
import { Effect, Either } from "effect"

const program = Effect.gen(function* () {
  const result = yield* fetchUser("123").pipe(Effect.either)

  if (Either.isLeft(result)) {
    // Handle error
    console.error("Error:", result.left)
    return null
  } else {
    // Handle success
    return result.right
  }
})
// Effect<User | null, never, never>

// Pattern matching on Either
const program2 = pipe(
  fetchUser("123"),
  Effect.either,
  Effect.map(
    Either.match({
      onLeft: (error) => ({ success: false, error }),
      onRight: (user) => ({ success: true, data: user })
    })
  )
)
```

### Effect.option - Convert to Option<Success>

Converts failures to None, success to Some:

```typescript
import { Effect, Option } from "effect"

const program = Effect.gen(function* () {
  const maybeUser = yield* fetchUser("123").pipe(Effect.option)

  if (Option.isNone(maybeUser)) {
    return guestUser
  } else {
    return maybeUser.value
  }
})
// Effect<User, never, never>

// Using Option.match
const program2 = pipe(
  fetchUser("123"),
  Effect.option,
  Effect.map(
    Option.match({
      onNone: () => "No user found",
      onSome: (user) => `Found: ${user.name}`
    })
  )
)
```

## Error Transformation

### Effect.mapError - Transform Error Types

```typescript
import { Effect, pipe } from "effect"

interface DbError {
  _tag: "DbError"
  code: string
  message: string
}

interface AppError {
  _tag: "AppError"
  message: string
  context: string
}

const program = pipe(
  queryDatabase(),
  Effect.mapError((dbError: DbError): AppError => ({
    _tag: "AppError",
    message: dbError.message,
    context: `Database operation failed: ${dbError.code}`
  }))
)

// Enriching errors with context
const enrichError = <E extends { message: string }>(
  context: string
) => (error: E) => ({
  ...error,
  message: `${context}: ${error.message}`
})

const programWithContext = pipe(
  fetchData(),
  Effe

Related in General