effect-patterns-error-handling
Effect-TS patterns for Error Handling. Use when working with error handling in Effect-TS applications.
Install
mkdir -p .claude/skills/effect-patterns-error-handling && curl -L -o skill.zip "https://mcp.directory/api/skills/download/3129" && unzip -o skill.zip -d .claude/skills/effect-patterns-error-handling && rm skill.zipInstalls to .claude/skills/effect-patterns-error-handling
About this skill
Effect-TS Patterns: Error Handling
This skill provides 3 curated Effect-TS patterns for error handling. Use this skill when working on tasks related to:
- error handling
- Best practices in Effect-TS applications
- Real-world patterns and solutions
🟡 Intermediate Patterns
Error Handling Pattern 1: Accumulating Multiple Errors
Rule: Use error accumulation to report all problems at once rather than failing early, critical for validation and batch operations.
Good Example:
This example demonstrates error accumulation patterns.
import { Effect, Data, Cause } from "effect";
interface ValidationError {
field: string;
message: string;
value?: unknown;
}
interface ProcessingResult<T> {
successes: T[];
errors: ValidationError[];
}
// Example 1: Form validation with error accumulation
const program = Effect.gen(function* () {
console.log(`\n[ERROR ACCUMULATION] Collecting multiple errors\n`);
// Form data
interface FormData {
name: string;
email: string;
age: number;
phone: string;
}
const validateForm = (data: FormData): ValidationError[] => {
const errors: ValidationError[] = [];
// Validation 1: Name
if (!data.name || data.name.trim().length === 0) {
errors.push({
field: "name",
message: "Name is required",
value: data.name,
});
} else if (data.name.length < 2) {
errors.push({
field: "name",
message: "Name must be at least 2 characters",
value: data.name,
});
}
// Validation 2: Email
if (!data.email) {
errors.push({
field: "email",
message: "Email is required",
value: data.email,
});
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
errors.push({
field: "email",
message: "Email format invalid",
value: data.email,
});
}
// Validation 3: Age
if (data.age < 0 || data.age > 150) {
errors.push({
field: "age",
message: "Age must be between 0 and 150",
value: data.age,
});
}
// Validation 4: Phone
if (data.phone && !/^\d{3}-\d{3}-\d{4}$/.test(data.phone)) {
errors.push({
field: "phone",
message: "Phone must be in format XXX-XXX-XXXX",
value: data.phone,
});
}
return errors;
};
// Example 1: Form with multiple errors
console.log(`[1] Form validation with multiple errors:\n`);
const invalidForm: FormData = {
name: "",
email: "not-an-email",
age: 200,
phone: "invalid",
};
const validationErrors = validateForm(invalidForm);
yield* Effect.log(`[VALIDATION] Found ${validationErrors.length} errors:\n`);
for (const error of validationErrors) {
yield* Effect.log(` ✗ ${error.field}: ${error.message}`);
}
// Example 2: Batch processing with partial success
console.log(`\n[2] Batch processing (accumulate successes and failures):\n`);
interface Record {
id: string;
data: string;
}
const processRecord = (record: Record): Result<string> => {
if (record.id.length === 0) {
return { success: false, error: "Missing ID" };
}
if (record.data.includes("ERROR")) {
return { success: false, error: "Invalid data" };
}
return { success: true, value: `processed-${record.id}` };
};
interface Result<T> {
success: boolean;
value?: T;
error?: string;
}
const records: Record[] = [
{ id: "rec1", data: "ok" },
{ id: "", data: "ok" }, // Error: missing ID
{ id: "rec3", data: "ok" },
{ id: "rec4", data: "ERROR" }, // Error: invalid data
{ id: "rec5", data: "ok" },
];
const results: ProcessingResult<string> = {
successes: [],
errors: [],
};
for (const record of records) {
const result = processRecord(record);
if (result.success) {
results.successes.push(result.value!);
} else {
results.errors.push({
field: record.id || "unknown",
message: result.error!,
});
}
}
yield* Effect.log(
`[BATCH] Processed ${records.length} records`
);
yield* Effect.log(`[BATCH] ✓ ${results.successes.length} succeeded`);
yield* Effect.log(`[BATCH] ✗ ${results.errors.length} failed\n`);
for (const success of results.successes) {
yield* Effect.log(` ✓ ${success}`);
}
for (const error of results.errors) {
yield* Effect.log(` ✗ [${error.field}] ${error.message}`);
}
// Example 3: Multi-step validation with error accumulation
console.log(`\n[3] Multi-step validation (all checks run):\n`);
interface ServiceHealth {
diskSpace: boolean;
memory: boolean;
network: boolean;
database: boolean;
}
const diagnostics: ValidationError[] = [];
// Check 1: Disk space
const diskFree = 50; // MB
if (diskFree < 100) {
diagnostics.push({
field: "disk-space",
message: `Only ${diskFree}MB free (need 100MB)`,
value: diskFree,
});
}
// Check 2: Memory
const memUsage = 95; // percent
if (memUsage > 85) {
diagnostics.push({
field: "memory",
message: `Using ${memUsage}% (threshold: 85%)`,
value: memUsage,
});
}
// Check 3: Network
const latency = 500; // ms
if (latency > 200) {
diagnostics.push({
field: "network",
message: `Latency ${latency}ms (threshold: 200ms)`,
value: latency,
});
}
// Check 4: Database
const dbConnections = 95;
const dbMax = 100;
if (dbConnections > dbMax * 0.8) {
diagnostics.push({
field: "database",
message: `${dbConnections}/${dbMax} connections (80% threshold)`,
value: dbConnections,
});
}
if (diagnostics.length === 0) {
yield* Effect.log(`[HEALTH] ✓ All systems normal\n`);
} else {
yield* Effect.log(
`[HEALTH] ✗ ${diagnostics.length} issue(s) detected:\n`
);
for (const diag of diagnostics) {
yield* Effect.log(` ⚠ ${diag.field}: ${diag.message}`);
}
}
// Example 4: Error collection with retry decisions
console.log(`\n[4] Error collection for retry strategy:\n`);
interface ErrorWithContext {
operation: string;
error: string;
retryable: boolean;
timestamp: Date;
}
const operationErrors: ErrorWithContext[] = [];
const operations = [
{ name: "fetch-config", fail: false },
{ name: "connect-db", fail: true },
{ name: "load-cache", fail: true },
{ name: "start-server", fail: false },
];
for (const op of operations) {
if (op.fail) {
operationErrors.push({
operation: op.name,
error: "Operation failed",
retryable: op.name !== "fetch-config",
timestamp: new Date(),
});
}
}
yield* Effect.log(`[OPERATIONS] ${operationErrors.length} errors:\n`);
for (const err of operationErrors) {
const status = err.retryable ? "🔄 retryable" : "❌ non-retryable";
yield* Effect.log(` ${status}: ${err.operation}`);
}
if (operationErrors.every((e) => e.retryable)) {
yield* Effect.log(`\n[DECISION] All errors retryable, will retry\n`);
} else {
yield* Effect.log(`\n[DECISION] Some non-retryable errors, manual intervention needed\n`);
}
});
Effect.runPromise(program);
Rationale:
Error accumulation strategies:
- Collect errors: Gather all failures before reporting
- Fail late: Continue processing despite errors
- Contextual errors: Keep error location/operation info
- Error summary: Aggregate for reporting
- Partial success: Return valid results + errors
Pattern: Use Cause aggregation, Result types, or custom error structures
Failing fast causes problems:
Problem 1: Form validation
- User submits form with 10 field errors
- Fail on first error: "Name required"
- User fixes name, submits again
- New error: "Email invalid"
- User submits 10 times before fixing all errors
- Frustration, reduced productivity
Problem 2: Batch processing
- Process 1000 records, fail on record 5
- 995 records not processed
- User manually retries
- Repeats for each error type
- Inefficient
Problem 3: System diagnostics
- Service health check fails
- Report: "Check 1 failed"
- Fix check 1, service still down
- Hidden problem: checks 2, 3, and 4 also failed
- Time wasted diagnosing
Solutions:
Error accumulation:
- Run all validations
- Collect errors
- Report all problems
- User fixes once, not 10 times
Partial success:
- Process all records
- Track successes and failures
- Return: "950 succeeded, 50 failed"
- No re-processing
Comprehensive diagnostics:
- Run all checks
- Report all failures
- Quick root cause analysis
- Faster resolution
🟠 Advanced Patterns
Error Handling Pattern 2: Error Propagation and Chains
Rule: Use error propagation to preserve context through effect chains, enabling debugging and recovery at the right abstraction level.
Good Example:
This example demonstrates error propagation with context.
import { Effect, Data, Cause } from "effect";
// Domain-specific errors with context
class DatabaseError extends Data.TaggedError("DatabaseError")<{
query: string;
parameters: unknown[];
cause: Error;
}> {}
class NetworkError extends Data.TaggedError("NetworkError")<{
endpoint: string;
method: string;
statusCode?: number;
cause: Error;
}> {}
class ValidationError extends Data.TaggedError("ValidationError")<{
field: string;
value: unknown;
reason: string;
}> {}
class BusinessLogicError extends Data.TaggedError("BusinessLogicError")<{
operation: string;
context: Record<string, unknown>;
originalError: Error;
}> {}
const program = Effect.gen(function* () {
console.log(`\n[ERROR PROPAGATION] Error chains with context\n`);
// Example 1: Simple error propagation
console.log(`[1] Error propagation through layers:\n`);
const lowLevelOperation = Effect.gen(function* () {
yield* Effect.log(`[LAYER 1] Low-level operatio
---
*Content truncated.*
More by PaulJPhilp
View all skills by PaulJPhilp →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
fastapi-templates
wshobson
Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.
Related MCP Servers
Browse all serversSync Trello with Google Calendar easily. Fast, automated Trello workflows, card management & seamless Google Calendar in
Claude Historian is a free AI search engine offering advanced search, file context, and solution discovery in Claude Cod
Claude Historian: AI-powered search for Claude Code conversations—find files, errors, context, and sessions via JSONL pa
Integrate with Google Drive and GCloud Storage via Google Cloud Platform for seamless access to Compute Engine, BigQuery
Easily enable Bitbucket and Jira integration with REST APIs for seamless repository management, pull requests, and works
Connect to the Brave Search API for fast web, image, and video results. Experience the power of the Brave search engine
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.