error management
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:
- Expected Errors (Recoverable) – Represented in the
Errortype parameter, tracked at compile time - 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
- Use TaggedError for all domain errors – Enables
catchTagpattern matching - Keep error channel for recoverable errors – Use defects for bugs
- Transform errors at boundaries – Map low-level errors to domain errors
- Use typed errors generously – The compiler tracks them for free
- Accumulate validation errors – Don’t fail fast when validating
- Only use Schema.Unknown for genuinely untyped values – The
causefield 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