error management

📁 andrueandersoncs/claude-skill-effect-ts 📅 Jan 1, 1970
8
总安装量
0
周安装量
#33822
全站排名
安装命令
npx skills add https://github.com/andrueandersoncs/claude-skill-effect-ts --skill Error Management

Skill 文档

Error Management in Effect

Overview

Effect distinguishes between two types of failures:

  1. Expected Errors (Recoverable) – Represented in the Error type parameter, tracked at compile time
  2. Defects (Unexpected/Unrecoverable) – Runtime exceptions, bugs, not in type signature
Effect<Success, Error, Requirements>
//              ^^^^^ Expected errors live here

Creating Typed Errors

Using Schema.TaggedError (Recommended)

import { Schema, Effect } from "effect"

class UserNotFound extends Schema.TaggedError<UserNotFound>()(
  "UserNotFound",
  { userId: Schema.String }
) {}

// Note: Schema.Unknown is semantically correct here because `cause` captures
// arbitrary caught exceptions whose type is genuinely unknown at the domain level.
// This is NOT type weakening - JavaScript exceptions can be any value.
class NetworkError extends Schema.TaggedError<NetworkError>()(
  "NetworkError",
  { cause: Schema.Unknown }
) {}

const getUser = (id: string): Effect.Effect<User, UserNotFound | NetworkError> =>
  Effect.gen(function* () {
    // ...implementation
    return yield* Effect.fail(new UserNotFound({ userId: id }))
  })

Using Effect.fail

const divide = (a: number, b: number) =>
  b === 0
    ? Effect.fail(new DivisionByZero())
    : Effect.succeed(a / b)

Catching and Recovering from Errors

catchAll – Catch All Errors

program.pipe(
  Effect.catchAll((error) =>
    Effect.succeed("fallback value")
  )
)

catchTag – Catch Specific Error by Tag

const program = getUser(id).pipe(
  Effect.catchTag("UserNotFound", (error) =>
    Effect.succeed(defaultUser)
  ),
  Effect.catchTag("NetworkError", (error) =>
    Effect.retry(Schedule.exponential("1 second"))
  )
)

catchTags – Handle Multiple Error Types

const program = getUser(id).pipe(
  Effect.catchTags({
    UserNotFound: (error) => Effect.succeed(defaultUser),
    NetworkError: (error) => Effect.fail(new ServiceUnavailable())
  })
)

orElse – Provide Fallback Effect

const primary = fetchFromPrimary()
const fallback = fetchFromBackup()

const resilient = primary.pipe(
  Effect.orElse(() => fallback)
)

orElseSucceed – Provide Fallback Value

const program = fetchConfig().pipe(
  Effect.orElseSucceed(() => defaultConfig)
)

Transforming Errors

mapError – Transform Error Type

const program = rawApiCall().pipe(
  Effect.mapError((error) => new ApiError({ cause: error }))
)

mapBoth – Transform Both Success and Error

const program = effect.pipe(
  Effect.mapBoth({
    onError: (e) => new WrappedError({ cause: e }),
    onSuccess: (a) => a.toUpperCase()
  })
)

Error Accumulation

When running multiple effects, collect all errors instead of failing fast:

Using Effect.all with mode: “either”

const results = yield* Effect.all(
  [effect1, effect2, effect3],
  { mode: "either" }
)

Using Effect.partition

const [failures, successes] = yield* Effect.partition(
  items,
  (item) => processItem(item)
)

Using Effect.validate

const result = yield* Effect.validate(
  [check1, check2, check3],
  { concurrency: "unbounded" }
)

Defects (Unexpected Errors)

Defects are bugs/unexpected failures not tracked in types:

const defect = Effect.die(new Error("Unexpected!"))

const program = effect.pipe(Effect.orDie)

const sandboxed = Effect.sandbox(program)

Cause – Full Error Information

The Cause type contains complete failure information:

import { Cause, Match } from "effect"

// In sandbox, you get full Cause - use Match for handling
const handled = Effect.sandbox(program).pipe(
  Effect.catchAll((cause) =>
    Match.value(cause).pipe(
      Match.when(Cause.isFailure, () => {
        // Expected error
        return Effect.succeed(fallback)
      }),
      Match.when(Cause.isDie, () => {
        // Defect - log and recover
        return Effect.succeed(fallback)
      }),
      Match.when(Cause.isInterrupt, () => {
        // Interruption
        return Effect.succeed(fallback)
      }),
      Match.orElse(() => Effect.succeed(fallback))
    )
  )
)

Retrying

import { Schedule } from "effect"

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

// Retry with condition - use Match.tag for error type checking
const conditional = effect.pipe(
  Effect.retry({
    schedule: Schedule.recurs(3),
    while: (error) =>
      Match.value(error).pipe(
        Match.tag("NetworkError", () => true),
        Match.orElse(() => false)
      )
  })
)

Timeouts

const withTimeout = effect.pipe(
  Effect.timeout("5 seconds")
)

const failOnTimeout = effect.pipe(
  Effect.timeoutFail({
    duration: "5 seconds",
    onTimeout: () => new TimeoutError()
  })
)

Error Matching Patterns

Using Effect.match

const result = yield* effect.pipe(
  Effect.match({
    onFailure: (error) => `Failed: ${error.message}`,
    onSuccess: (value) => `Success: ${value}`
  })
)

Using Effect.matchEffect

const result = yield* effect.pipe(
  Effect.matchEffect({
    onFailure: (error) => logError(error).pipe(Effect.as("failed")),
    onSuccess: (value) => logSuccess(value).pipe(Effect.as("success"))
  })
)

Best Practices

  1. Use TaggedError for all domain errors – Enables catchTag pattern matching
  2. Keep error channel for recoverable errors – Use defects for bugs
  3. Transform errors at boundaries – Map low-level errors to domain errors
  4. Use typed errors generously – The compiler tracks them for free
  5. Accumulate validation errors – Don’t fail fast when validating
  6. Only use Schema.Unknown for genuinely untyped values – The cause field on error types is the canonical example (caught JS exceptions can be any value). Never use Schema.Unknown or Schema.Any for fields whose shape you can describe – define proper schemas instead.

Additional Resources

For comprehensive error management documentation, consult ${CLAUDE_PLUGIN_ROOT}/references/llms-full.txt.

Search for these sections:

  • “Expected Errors” for creating typed errors
  • “Error Accumulation” for collecting multiple errors
  • “Sandboxing” for handling defects
  • “Retrying” for retry policies
  • “Timing Out” for timeout patterns
  • “Two Types of Errors” for error philosophy