effect-error-handling
Use when Effect error handling patterns including catchAll, catchTag, either, option, and typed errors. Use for handling expected errors in Effect applications.
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(),
EffeRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.