# Handle expected failures without hiding defects

> For agents: start with the [agent guide](https://ratstack.sh/llms.txt). Every page is Markdown by default; add `Accept: text/html` for HTML.

New on /news: [Effect Office Hours 52 with Resonate HQ](/news).

A defect is an unexpected execution failure.

Declare failures that callers can handle. Keep defects distinct from those declared outcomes.
Catch a failure where the application can recover or report it truthfully.

## Give expected failures a type and useful context

`Schema.TaggedError` defines a yieldable error: an error a generator can raise with `yield*`.
Its tag identifies the error type; its fields retain context.
`FileStatsError` keeps the path and the filesystem reason.
The brand stops two classes with the same fields from being assignable to each other.

packages/core/src/stats.ts at c936b94c957ada7199df3ab7b39a330d6e1c644f; lines 13-26; highlighted none.

```typescript
export class FileStatsError extends Schema.TaggedError<
  FileStatsError,
  { readonly brand: unique symbol }
>()("FileStatsError", {
  path: Schema.String,
  reason: Schema.String,
}) {
  override readonly [Runtime.errorExitCode] = 1;
  override readonly [Runtime.errorReported] = false;

  override get message(): string {
    return `Could not read ${this.path}: ${this.reason}`;
  }
}
```

Inside a generator, `return yield*` can raise a yieldable error directly.
`Effect.fail` can also place an error in the expected failure channel, the `E` parameter of the Effect type.
Do not throw an ordinary exception to represent a declared refusal.

## Translate implementation failures without losing their meaning

The inspector uses `Effect.mapError` to translate a filesystem failure into its domain error.

packages/core/src/file-inspector.ts at 65e9465f38092e24486392f22c08f45d61230c20; lines 20-26; highlighted none.

```typescript
      const content = yield* fileSystem
        .readFile(path)
        .pipe(
          Effect.mapError(
            (error) => new FileStatsError({ path, reason: error.message })
          )
        );
```

Translation keeps the failed path and the source message. Callers receive `FileStatsError`, not a filesystem dependency.

If a domain error needs to carry an unknown source error, `Schema.Defect()` can describe that field.
Such a field does not itself turn an expected error into a runtime [defect](/lore/effect-basics).

## Recover or report at a named boundary

`Effect.catchTag` handles the selected failure tag. Other failures remain failed.
Use `catchTags` when several tagged failures need different handlers.

The CLI reports the inspection failure, then fails again:

apps/cli/src/cli.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 19-21; highlighted none.

```typescript
  Effect.catchTag("FileStatsError", (error) =>
    Console.error(error.message).pipe(Effect.andThen(Effect.fail(error)))
  ),
```

A fallback is appropriate only when its value is a valid domain result.
Do not turn a failed inspection into empty file statistics just to complete the command.

## Do not use orDie to hide ordinary failures

`Effect.orDie` removes expected failures from the typed channel by turning them into defects.
The inspection [machine](/lore/lifecycles-are-machines) uses it where the machine's result is handed back to Effect because its expected file failure already travels through the `Unreadable` outcome.
An unexpected actor failure or missing outcome is a programming failure at that boundary.

This is a specific boundary decision. File reads still return `FileStatsError` to ordinary callers.
Use `Effect.catchDefect` only where an integration boundary must inspect unexpected failures.
Do not treat it as general domain recovery.

## Common wrong approach

Catching every cause and returning success erases whether the program refused work or crashed.
A later caller cannot tell which repair is needed.
Keep refusal, unexpected failure, and successful completion distinct.

## Effect idiom and house rule

- Expected failures appear in the [Effect](/lore/effect-basics) type. Tagged handlers can recover without swallowing defects.
- In rat-stack, errors name their source and give repair guidance. Finite lifecycles use XState through the pinned Effect bridge.
- The application chooses which lifecycles need machines and which diagnostic fields each error needs.

See also: [schemas](/lore/schemas-define-the-boundary), [author-facing diagnostics](/lore/errors-are-part-of-the-interface), [independent failures](/lore/accumulate-independent-errors), and [lifecycles](/lore/lifecycles-are-machines). [Failures keep their meaning](/lore/failures-keep-their-meaning) maps failure kinds to retry, refusal, and escalation.

## Sources

1. [Arnaldi, Michael. n.d. Why JavaScript Is Broken: How Effect and TypeScript Fixes It.](https://youtu.be/und9h4xiIz4?t=1418)
   Michael Arnaldi. Historical explanation at 23:38 distinguishes failures intended for control flow from defects. Accessed 2026-10-06.

2. [Arnaldi, Michael. n.d. Why JavaScript Is Broken: How Effect and TypeScript Fixes It.](https://youtu.be/und9h4xiIz4?t=1512)
   Michael Arnaldi. Historical defect explanation at 25:12; not evidence for current API names. Accessed 2026-10-06.

3. [Effect contributors. 2026. Schema. Effect 4.0.0.](https://github.com/Effect-TS/effect/blob/67ba4e46a11ccda0b6761578bfd22c04ae00167d/packages/effect/src/Schema.ts)
   Effect-TS. TaggedError produces yieldable errors; Defect() describes unknown error values in a schema. Accessed 2026-10-06.

4. [Effect contributors. 2026. Effect. Effect 4.0.0.](https://github.com/Effect-TS/effect/blob/67ba4e46a11ccda0b6761578bfd22c04ae00167d/packages/effect/src/Effect.ts)
   Effect-TS. mapError translates expected failures; catchTag selects tagged failures; catchDefect and orDie handle different failure boundaries. Accessed 2026-10-06.

5. [rat-stack contributors. 2026. File statistics and failures.](https://github.com/joelhooks/rat-stack/blob/c936b94c957ada7199df3ab7b39a330d6e1c644f/packages/core/src/stats.ts)
   rat-stack. FileStatsError retains the failed path and filesystem reason. Accessed 2026-10-06.

6. [rat-stack contributors. 2026. CLI failure boundary.](https://github.com/joelhooks/rat-stack/blob/65e9465f38092e24486392f22c08f45d61230c20/apps/cli/src/cli.ts)
   rat-stack. The CLI reports a FileStatsError and keeps it failed instead of returning success. Accessed 2026-10-06.

7. [rat-stack contributors. 2026. Inspection lifecycle.](https://github.com/joelhooks/rat-stack/blob/65e9465f38092e24486392f22c08f45d61230c20/packages/core/src/inspect-machine.ts)
   rat-stack. Expected read failure travels through a domain outcome; unexpected actor failure becomes a defect. Accessed 2026-10-06.

8. [Langton, Kit. 2026. Error Handling. Effect Solutions.](https://github.com/kitlangton/effect-solutions/blob/09f82e6c5c928e7232cd32daf04d7c6a830b63f7/packages/website/docs/06-error-handling.md)
   Kit Langton. Topic framing for expected failures and defects; older TaggedErrorClass and catchAllDefect names are not used here. Accessed 2026-10-06.

## Lore on this page

- [Failures keep their meaning](/lore/failures-keep-their-meaning)

## Linked from

- A service captures its construction dependencies → Build a service with its dependencies, then expose methods that callers can use without rebuilding the graph. → [Read page](https://ratstack.sh/lore/services-capture-dependencies)

- add-a-capability → Learn how one contract and its handler become a command, HTTP route, MCP tool, browser RPC, and sandbox call. → [Read page](https://ratstack.sh/skills/add-a-capability)

- add-a-lifecycle-machine → Learn how XState owns a lifecycle while Effect owns its work, errors, and services. → [Read page](https://ratstack.sh/skills/add-a-lifecycle-machine)

- add-a-store → Add persistence through a service named for an application job, with validation, vendor Layers, migrations, bounded reads, and shared backend tests. → [Read page](https://ratstack.sh/skills/add-a-store)

- Agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms.txt)

- Analytics → One event per HTTP request, stored unchanged for queries to select and interpret later. → [Read page](https://ratstack.sh/systems/analytics)

- Build and run rat-stack (README.md) → What is in the repo, how the example works, and how to run it. → [Read page](https://ratstack.sh/README.md)

- Capabilities → Named application actions exposed through generated interfaces share one definition and one server-side implementation. → [Read page](https://ratstack.sh/systems/capabilities)

- Change log → What changed in the files served here, newest first. → [Read page](https://ratstack.sh/log)

- Cleanup can be an idempotent postcondition → Already-absent state can be success when absence is the operation’s intended result. → [Read page](https://ratstack.sh/lore/cleanup-can-be-an-idempotent-postcondition)

- Code snippets quote a pinned source → Shipped design brief: pinned Git objects, typed diagnostics, and one build-only snippet model for both views. → [Read page](https://ratstack.sh/lore/code-snippets)

- Configuration is a supplied, validated dependency → Parse configuration when a Layer builds, reject invalid values, and supply controlled settings in tests. → [Read page](https://ratstack.sh/lore/configuration-is-a-dependency)

- Database → One interface records action runs and their outcomes in either Cloudflare D1 or Postgres through Hyperdrive. → [Read page](https://ratstack.sh/systems/database)

- Devtools → Development-only capabilities inspect calls and lifecycle transitions without entering production bundles. → [Read page](https://ratstack.sh/systems/devtools)

- Durability starts with a recovery record → A reading order for storage, replay, worker ownership, overload, and recovery tests. → [Read page](https://ratstack.sh/lore/durability)

- Effect + Alchemy peer patterns → Source-grounded patterns from repos on nearby Effect and Alchemy pins. → [Read page](https://ratstack.sh/resources/same-version-repos)

- Effect basics → An Effect describes work, expected failures, and required services before a runtime executes it. → [Read page](https://ratstack.sh/lore/effect-basics)

- Effect Office Hours 27: Alchemy, infrastructure as effects → Sam Goodwin presents Alchemy as a system built with Effect for planning, testing, and deploying infrastructure. → [Read page](https://ratstack.sh/lore/effect-office-hours-27-alchemy-infrastructure-as-effects)

- Errors are part of the interface → An author error needs a location, the rejected input, and a repair. → [Read page](https://ratstack.sh/lore/errors-are-part-of-the-interface)

- Failures keep their meaning → Expected refusals, infrastructure faults, and defects need different policies. → [Read page](https://ratstack.sh/lore/failures-keep-their-meaning)

- Fence → Strict effect-tsrs diagnostics, lint, and hooks protect the gate; patched tsc checks compiler parity on checker bumps. → [Read page](https://ratstack.sh/systems/fence)

- find-peers → Refresh public peers, exact stack versions, and tiers after stack bumps. On gardener passes, refresh peers checked more than 14 days ago without blocking other work. → [Read page](https://ratstack.sh/skills/find-peers)

- Full agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms-full.txt)

- gardener → Keep rat-stack current and clean. Update exact dependency versions, learn from repos on the same versions, and add lint rules before cleanup. → [Read page](https://ratstack.sh/skills/gardener)

- HTTP success needs a status check and a decoder → Supply the client, check the response status, decode accepted data, and name failures at the adapter boundary. → [Read page](https://ratstack.sh/lore/http-responses-need-validation)

- keep-or-cut → Learn which pieces depend on each other, then keep only the ones your project needs. → [Read page](https://ratstack.sh/skills/keep-or-cut)

- Learn mode presents concepts at useful depths → An agent presents source-derived concepts during real work and keeps learning events in a private local log. → [Read page](https://ratstack.sh/systems/learn)

- learn-alchemy → Learn how Alchemy 2 turns an Effect program into a planned Cloudflare deployment. → [Read page](https://ratstack.sh/skills/learn-alchemy)

- learn-rat-stack → Trace a capability through Effect, XState, five projections, the CLI, and Alchemy. Try hosted search and read. → [Read page](https://ratstack.sh/skills/learn-rat-stack)

- Rat Stack lore | rat-stack → Short, source-grounded notes on the ideas and decisions behind rat-stack. → [Read page](https://ratstack.sh/lore)

- Run Effect at the integration boundary → Build the runtime where a calling framework enters, reuse its services, and dispose it when that integration ends. → [Read page](https://ratstack.sh/lore/run-effect-at-the-boundary)

- Schemas define the boundary → Define accepted data with Schema, decode unknown input before dispatch, and encode results through the declared contract. → [Read page](https://ratstack.sh/lore/schemas-define-the-boundary)

- Scopes release resources when work ends → Acquire resources lazily, keep their use inside the owner, and release them after success, failure, or interruption. → [Read page](https://ratstack.sh/lore/scopes-own-resources)

- ship → Learn how to ship a change through merge, CI, release, stage deployment, rollout, verification, rollback and flags. → [Read page](https://ratstack.sh/skills/ship)

- Source change history (log.md) → The generated change log as Markdown. → [Read page](https://ratstack.sh/log.md)

- Structure an Effect codebase by domain → Start with folders for application work, services and Layers in the same file, and startup code that supplies implementations. → [Read page](https://ratstack.sh/lore/structure-effect-by-domain)

- uncomplect → Find responsibilities that a rat-stack design combines, separate them into capabilities, cartridges, machines, features, and clients, and enforce that separation through types and checks. Use when asked to simplify, review, or replace a design, "what would Rich Hickey do", "optimize for deletion", or "define this error out of existence". → [Read page](https://ratstack.sh/skills/uncomplect)

- Why TypeScript is a good fit for Scala developers → John De Goes and Michael Arnaldi discuss carrying functional programming ideas into TypeScript. → [Read page](https://ratstack.sh/lore/ziverge-de-goes-arnaldi-typescript-for-scala-devs)

## Unlinked mentions

- [Effect basics](https://ratstack.sh/lore/effect-basics) → Meaning: Expected failure¶
