effect-errors
Effect error handling, for expected errors, defects and interruptions.
Skill by andabove
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/andabove-effect-errors
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/effect-errors
What's inside
- Skill name
effect-errors- Description
- Effect error handling, for expected errors, defects and interruptions. Use when you define or raise a tagged error, recover with `Effect.catch*`, read a `Cause` or `Exit`, set a retry or timeout policy, or decide whether a failure is an error or a defect.
- License
- MIT
- Instructions
- 236 lines in SKILL.md · view on GitHub
Files (4)
- SKILL.md
- effect-errors-cause-and-exit.md · references/
- effect-errors-operators.md · references/
- effect-errors-retry-timeout.md · references/
SKILL.md (first lines)
# Effect errors
Checked against `effect` 4.0.0. When the project has a newer version, check each API in `node_modules/effect/src/Effect.ts`, `Cause.ts` and `Exit.ts`, and read `node_modules/effect/AGENTS.md`. Load the `effect` skill too: it covers creating effects, `Effect.gen` and running.
## Three ways an effect can end badly
| Kind | What it means | Where it is | Typical source |
| --- | --- | --- | --- |
| **Failure** (expected error) | part of the domain: invalid input, not found, rate limited | the `E` in `Effect<A, E, R>` | `Effect.fail`, `yield* new SomeError(...)`, `Effect.try` with `catch` |
| **Defect** | a bug or a broken invariant | only in the `Cause`, as a `Die` reason | a throw in `Effect.sync`, a rejection in `Effect.promise`, `Effect.die`, `Effect.orDie` |
| **Interruption** | someone stopped the fiber | only in the `Cause`, as an `Interrupt` reason | a timeout, a lost race, `SIGINT` under `runMain`, `Fiber.interrupt` |
Decide by the caller: if a caller can do something useful about it (retry, fall back, show a message, map to an HTTP status), make it a failure in `E`. If no caller can, make it a defect and let it reach the edge, where it is logged.
## Define errors as tagged classes
```ts
import * as Data from "effect/Data"
import * as Effect from "effect/Effect"
import * as Schema from "effect/Schema"
export class UserNotFound extends Schema.TaggedError<UserNotFound>()("UserNotFound", {
userId: Schema.String,
message: Schema.String
}) {}
export class StoreUnavailable extends Data.TaggedError("StoreUnavailable")<{
readonly cause: unknown
}> {
override get message() {
return "user store is unavailable"
}
}
declare const queryUser: (id: string) => Effect.Effect<{ readonly name: string } | undefined, StoreUnavailable>
export const findUser = Effect.fn("findUser")(function*(userId: string) {
const user = yield* queryUser(userId)
if (user === undefined) {
return yield* new UserNotFound({ userId, message: `no user with id ${userId}` })
}
return user
})
```
- Use `Schema.TaggedError` when the error crosses a boundary that encodes it: an HTTP API, RPC, a workflow, a queue, a log sink that needs JSON. Use `Data.TaggedError` for errors that stay in the process. `effect/Data` loads 15 modules, `effect/Schema` 113.
- Give each error a readable `message`: a `message` field, or a `get message()` override. Without one, `error.message` is the empty string and the logs say only the tag.
- Keep the original failure in a `cause` field (`cause: Schema.Defect()` in a schema error, `cause: unknown` in a data error). In a `Data.TaggedError` the field becomes the standard `Error.cause`.
- Make each `_tag` unique in the codebase. `catchTag` matches by the string, so two classes with one tag catch each other.
- Raise with `return yield* new UserNotFound({ ... })` in a generator, or `Effect.fail(new UserNotFound({ ... }))` in a pipeline. A tagged error instance is itself yieldable.
- Do not use strings or plain `Error` as `E`: callers cannot tell them apart with `catchTag`.
## Keep the error type honest
A declared error type can hide a missing case. A narrower error type fits a wider declared one, so when a change drops a case (a `NotFound` becomes a generic `StoreError`, and a 404 becomes a 500), a function declared with `Effect.fn.Return<A, NotFound | StoreError>` still compiles, and so does every caller.
- Let `Effect.fn` infer the error type from the body.
- Pin each public signature with a type test, `expectTypeOf` in a `*.test-d.ts` file. `toEqualTypeOf` fails when the inferred union loses a case or gains one.
```ts
import * as Data from "effect/Data"
import * as Effect from "effect/Effect"
import { expectTypeOf } from "vitest"
class NotFound extends Data.TaggedError("NotFound")<{ readonly id: string }> {}
class StoreError extends Data.TaggedError("StoreError")<{ readonly cause: unknown }> {}
interface Item { readonly id: string }
declare const query: (id: string) => Effect.Effect<Item | undefined, StoreError>
// No declared return type: the error type comes from the body.
export const getItem = Effect.fn("getItem")(function*(id: string) {
const row = yield* query(id)
if (row === undefined) return yield* new NotFound({ id })
return row
})
// getItem.test-d.ts: fails to compile when the body stops raising NotFound.
expectTypeOf(getItem).returns.toEqualTypeOf<Effect.Effect<Item, NotFound | StoreError>>()
```
Checked with `tsc` on 4.0.0. With `NotFound` replaced by `StoreError` in the body, the declared form compiles, and the type test fails with `TS2344`. See `effect-testing` for running type tests.
## Recover
Pick the narrowest operator. Each one removes what it handles from `E`.
| You want to | Write |
| --- | --- |
| handle one tagged error | `Effect.catchTag("UserNotFound", (e) => ...)` |
| handle several tags with one handler | `Effect.catchTag(["UserNotFound", "Forbidden"], (e) => ...)` |
| handle several tags, each its own way | `Effect.catchTags({ UserNotFound: (e) => ..., Forbidden: (e) => ... })` |
| handle errors that match a predicate | `Effect.catchIf((e) => ..., (e) => ...)` |
| handle every expected error | `Effect.catch((e) => ...)` |
| handle one nested `reason` of a tagged error | `Effect.catchReason("AiError", "RateLimited", (r) => ...)` |
| replace any error with a value | `Effect.orElseSucceed(() => fallback)` |
| turn the error into a value | `Effect.result` (gives a `Result`), `Effect.option` (drops the error) |
| change the error type | `Effect.mapError((e) => new OtherError({ cause: e }))` |
| observe without handling | `Effect.tapError`, `Effect.tapErrorTag`, `Effect.tapCause` |
| give up: make it a defect | `Effect.orDie` |
What each operator sees when the cause holds one reason (proved against 4.0.0; for a cause with a failure and a defect, see **Mixed causes** below):
| Operator | Failure | Defect | Interruption |
| --- | --- | --- | --- |
| `catch`, `catchTag(s)`, `catchIf`, `result`, `option`, `match`, `ignore`, `orElseSucceed`, `retry`, `tapError` | yes | no | no |
| `catchDefect`, `tapDefect` | no | yes | no |
| `catchCause`, `matchCause`, `exit`, `sandbox`, `ignoreCause`, `tapCause`, `onError` | yes | yes | yes |
Traps:
- `Effect.catch` does not catch a defect on its own. A throw inside `Effect.sync` or a rejection inside `Effect.promise` passes through every typed handler. Wrap code that can throw with `Effect.try` or `Effect.tryPromise` and a `catch`.
- `Effect.ignore` and `Effect.result` keep a defect that comes alone. `Effect.ignoreCause` drops it; use it only where losing a bug report is acceptable.
- `catchCause` and `matchCause` also see an interruption that the effect raised itself (`Effect.interrupt`). If you recover from every cause, check `Cause.hasInterruptsOnly(cause)` first and re-raise it with `Effect.failCause(cause)`. An interruption from outside the fiber cannot be caught: the handler does not run.
- `try`/`catch` in an `Effect.gen` body does not see a failed effect (see the `effect` skill). Use the operators above.
- Recover from defects (`catchDefect`, `catchCause`) only at a boundary: a request handler that must answer 500, a plugin host, a worker loop that must survive one bad job.
### Mixed causesFrom andabove/skills, MIT.
More from andabove/skills
- 0.570score
effect-testing
andabove
Testing Effect code with @effect/vitest, TestClock and test layers.
SkillAFreshness: AClaude Code +9 - 0.570score
effect
andabove
Effect (the TypeScript library) core and router to the other Effect skills.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-adoption
andabove
Adopting Effect in an existing Promise codebase one module at a time.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-concurrency
andabove
Effect concurrency - fibers, interruption, coordination and Schedule.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-data
andabove
Effect 4 data types, equality, caching and batching.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-observability
andabove
Effect 4 logging, tracing and metrics, and their export.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-schema
andabove
Effect 4 Schema for parsing and modelling data.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-services
andabove
Effect 4 services, layers and resources.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-streams
andabove
Effect Stream, Sink and Channel.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-v3-to-v4
andabove
Effect v3 to v4 migration.
SkillAFreshness: AClaude Code +9 - 0.565score
linear-comment
andabove
Write a comment on a Linear issue as a status record against its acceptance criteria and QA notes.
SkillAFreshness: AClaude Code +9 - 0.565score
code-style
andabove
Fifteen TypeScript code rules for modules, names, doc comments, types, boundaries, errors and side effects.
SkillAFreshness: AClaude Code +9
Signals
- Stars
- 0
- Forks
- 0
- Open issues
- 0
- Last commit (this folder)
- Oct 3, 2026 (26 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/andabove/skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from andabove
- 0.570score
effect-data
andabove
Effect 4 data types, equality, caching and batching.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-concurrency
andabove
Effect concurrency - fibers, interruption, coordination and Schedule.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-testing
andabove
Testing Effect code with @effect/vitest, TestClock and test layers.
SkillAFreshness: AClaude Code +9 - 0.570score
effect
andabove
Effect (the TypeScript library) core and router to the other Effect skills.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-adoption
andabove
Adopting Effect in an existing Promise codebase one module at a time.
SkillAFreshness: AClaude Code +9 - 0.570score
effect-observability
andabove
Effect 4 logging, tracing and metrics, and their export.
SkillAFreshness: AClaude Code +9
Cite this page
effect-errors skill. Agent Mart. https://agentmart.dev/skills/andabove-effect-errors (last updated 2026-10-24)