ER

error-handling

Replaces verbose try-catch blocks with standardized Result types and graceful recovery patterns.

Install

mkdir -p .claude/skills/error-handling && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/3309" && unzip -o skill.zip -d .claude/skills/error-handling && rm skill.zip

Installs to .claude/skills/error-handling

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Error handling with wellcrafted trySync/tryAsync and toastOnError. Use for try-catch, Result types, error toasts, HTTP errors.
126 chars✓ has a “when” trigger
Intermediate

Key capabilities

  • Transforms try-catch into Result objects
  • Standardizes HTTP error status codes
  • Integrates with UI toast systems
  • Encapsulates domain-specific errors

How it works

Uses functional wrappers to intercept exceptions and return discriminated union types for success or failure.

Inputs & outputs

You give it
Asynchronous function or block
You get back
Result object or standardized error response

When to use error-handling

  • Refactor nested try-catch blocks
  • Implement Result types for function returns
  • Standardize HTTP error status responses
  • Add consistent error toast notifications

About this skill

Error Handling

This skill owns the boundary between thrown exceptions and Result values, plus correct Result consumption. Compose with define-errors for variant design, logging for diagnostics, query-layer for RPC presentation, and hono for response APIs.

Source Of Truth

Ground every Wellcrafted behavior claim in the official wellcrafted-dev/wellcrafted source and tests. When maintaining this guidance, confirm that Epicenter's installed version matches the source being read. If it does not, report the version drift; dependency freshness is handled outside this skill. Treat other skills, examples, generated documentation, and DeepWiki as leads, not authority.

Read the scoped references only when needed:

Choose The Contract First

Required contractPattern
Caller receives Result<T, E>Adapt the throwing operation with trySync or tryAsync
Failure has a valid fallbackReturn Ok(fallback) from catch
Failure must propagate as dataReturn a typed defineErrors factory result from catch
Only known external failures should become ErrMap known exceptions and rethrow unknown ones
Surrounding API is exception-basedKeep try-catch, or unwrap only at that boundary
Cleanup must run on success, failure, cancellation, or early returnUse finally

Use trySync for a synchronous operation and tryAsync for an operation returning a Promise.

const { data: response, error } = await tryAsync({
	try: () => fetch(url),
	catch: (cause) => RequestError.TransportFailed({ cause }),
});
if (error !== null) return Err(error);
return Ok(response);

defineErrors factories already return Err(...). Pass the raw cause into the factory and let the factory compose its message with extractErrorMessage. Do not use raw Err(cause) at a catch boundary: thrown values may be null or undefined, and an untyped cause loses the domain failure.

Consume Every Possible Err Branch

  • If a value can be Result<T, E>, inspect or deliberately forward its error branch.
  • After destructuring, error is the raw E. Return Err(error), not error.
  • If you retained the whole Result, return it unchanged: if (result.error !== null) return result.
  • error !== null is the reliable discriminator. Never construct Err(null) or Err(undefined).
  • Data-only destructuring is correct when the catch branch always returns Ok<T> and the inferred type collapses to Ok<T>.
  • Error-only destructuring is correct when success data is irrelevant. The rule is to handle every possible Err, not to destructure fields you do not use.

Prefer an immediate guard so the success path stays linear.

Own The Promise

tryAsync returns a Promise. Choose its owner explicitly:

  • await when this function inspects the Result.
  • return tryAsync(...) when the caller owns the Promise<Result<...>>.
  • Do not use bare void tryAsync(...): ordinary failures fulfill with Err, so a Promise rejection handler cannot observe them. A best-effort operation still needs an async owner that awaits the Result and explicitly logs or ignores its error branch.
  • In UI fire-and-forget code, attach presentation before discarding a Promise that fulfills with a Result: void save().then((result) => toastOnError(result, 'Save failed')). If the Promise can reject, adapt or catch that rejection first.

Keep Native Try-Catch When It Expresses The Contract

Traditional try-catch is appropriate when:

  • a finally block owns cleanup;
  • a generator must yield a failure rather than return a Result;
  • a framework boundary converts an exception directly into its required response shape;
  • code catches one known exception and rethrows everything else;
  • the surrounding public API intentionally throws.

Do not turn unknown programming errors into a generic domain failure. Mapping every throw to Err can hide bugs behind a misleading retryable error.

Final Check

  1. The function's throw-versus-Result contract is explicit.
  2. The wrapper covers one coherent failure meaning.
  3. Caught values become typed errors, intentional fallbacks, or selective rethrows.
  4. Every possible Err branch is handled, forwarded, logged, presented, or explicitly ignored by a named best-effort owner.
  5. Unknown bugs still reach the appropriate crash or framework error boundary.

When not to use it

  • Critical system-level crash recovery
  • Simple scripts without external dependencies

Prerequisites

Wellcrafted library

Limitations

  • Requires library adoption
  • Adds overhead to simple control flows

How it compares

Forces explicit failure handling rather than relying on global error boundaries or silent failures.

Compared to similar skills

error-handling side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
error-handling (this skill)22moNo flagsIntermediate
javascript-mastery76moNo flagsBeginner
tech-debt-analyzer59moReviewIntermediate
codex-code-review17moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

javascript-mastery

davila7

Comprehensive JavaScript reference covering 33+ essential concepts every developer should know. From fundamentals like primitives and closures to advanced patterns like async/await and functional programming. Use when explaining JS concepts, debugging JavaScript issues, or teaching JavaScript fundamentals.

731

tech-debt-analyzer

ailabs-393

This skill should be used when analyzing technical debt in a codebase, documenting code quality issues, creating technical debt registers, or assessing code maintainability. Use this for identifying code smells, architectural issues, dependency problems, missing documentation, security vulnerabilities, and creating comprehensive technical debt documentation.

522

codex-code-review

tyrchen

Perform comprehensive code reviews using OpenAI Codex CLI. This skill should be used when users request code reviews, want to analyze diffs/PRs, need security audits, performance analysis, or want automated code quality feedback. Supports reviewing staged changes, specific files, entire directories, or git diffs.

16

dead-code

parcadei

Find unused functions and dead code in the codebase

16

typescript-circular-dependency

blader

Detect and resolve TypeScript/JavaScript circular import dependencies. Use when: (1) "Cannot access 'X' before initialization" at runtime, (2) Import returns undefined unexpectedly, (3) "ReferenceError: Cannot access X before initialization", (4) Type errors that disappear when you change import order, (5) Jest/Vitest tests fail with undefined imports that work in browser.

14

tech-debt

vm0-ai

Technical debt management - scan codebase for bad smells and create tracking issues

13

Search skills

Search the agent skills registry