# Expected vs Unexpected Errors

Effect splits every failure into one of two categories, and the distinction
drives the entire API. Getting it right is the single most important idea in
error management.

- A **failure** is an *expected* error — something your domain knows can happen
  and is prepared to handle. Failures live in the `E` channel of
  `Effect<A, E, R>`, so the compiler tracks them and won't let you forget one.

- A **defect** is an *unexpected* error — a bug, a broken invariant, a thrown
  exception. Defects are *not* tracked in the type. They surface at the top of
  your program (or wherever you explicitly choose to catch them), not at every
  call site.

```ts
import { Effect, Schema } from "effect"

class UserNotFound extends Schema.TaggedErrorClass<UserNotFound>()(
  "UserNotFound",
  { id: Schema.Number }
) {}

// A FAILURE: `UserNotFound` is expected, so it appears in the error channel.
//      ┌─── Effect<User, UserNotFound>
//      ▼
const findUser = (id: number) =>
  id > 0
    ? Effect.succeed({ id, name: "Ada" })
    : Effect.fail(new UserNotFound({ id }))

// A DEFECT: a violated invariant we never expect to hit. `Effect.die` records
// it as a defect, so the error channel stays `never` — callers are not asked
// to handle it.
//      ┌─── Effect<number, never>
//      ▼
const half = (n: number) =>
  n % 2 === 0
    ? Effect.succeed(n / 2)
    : Effect.die(new Error(`expected an even number, got ${n}`))
```

## Why two channels?

The error channel is a contract. If `findUser` can fail with `UserNotFound`,
every caller sees that in the type and the compiler refuses to let the program
type-check until the failure is handled or propagated. This is what makes Effect
error handling *exhaustive* — you cannot silently drop an expected error.

Defects are the opposite: they represent situations that *shouldn't* happen. You
don't want to thread "the JSON parser had a bug" through every function
signature. Instead, the runtime carries defects invisibly until something
chooses to inspect them — typically a logging boundary at the edge of your app.
**Note:** Both failures and defects are preserved by the runtime inside a
  [`Cause`](https://effect.plants.sh/error-management/result-and-exit/). A defect is never lost — it is
  simply not surfaced in the type. You can always recover it with
  `Effect.catchCause` or observe it in an `Exit`.

## Creating failures and defects

```ts
import { Effect } from "effect"

// Failures — tracked in E
Effect.fail("boom") // Effect<never, string>

// Defects — NOT tracked in E
Effect.die("boom") // Effect<never, never>

// `Effect.sync` assumes the thunk never throws — if it does, the thrown value
// becomes a DEFECT.
Effect.sync(() => JSON.parse("{ not json")) // Effect<any, never>

// `Effect.try` catches the throw and turns it into a typed FAILURE.
Effect.try({
  try: () => JSON.parse("{ not json"),
  catch: (cause) => new Error(String(cause))
}) // Effect<any, Error>
```

`Effect.try` (and its async sibling `Effect.tryPromise`) is the standard way to
bring throwing code into Effect *as a failure*. `Effect.sync` is for code you
*promise* won't throw — if it does anyway, that's a bug, so it surfaces as a
defect.

## Moving between the channels

Sometimes a failure turns out to be unrecoverable, or a defect needs to be
promoted to a handled failure. Effect gives you explicit converters so the
transition is always visible in the type.

```ts
import { Effect, Schema } from "effect"

class ConfigError extends Schema.TaggedErrorClass<ConfigError>()(
  "ConfigError",
  { message: Schema.String }
) {}

declare const loadConfig: Effect.Effect<string, ConfigError>

// `orDie` says: this failure can never be recovered — treat it as a defect.
// The error channel collapses from `ConfigError` to `never`.
const required: Effect.Effect<string> = loadConfig.pipe(Effect.orDie)
```

Use `Effect.orDie` when a typed error is, in context, truly fatal — for example
config that *must* be present at startup. Going the other way (defect →
failure) is done by catching the cause, which the
[catching errors](https://effect.plants.sh/error-management/catching-errors/) page covers.
**Tip:** A good rule of thumb: if a caller could *reasonably do something* about an
  error, make it a failure. If the only sane response is "crash and alert a
  human", make it a defect.

## Next steps

- Define rich, matchable failures with
  [Tagged Errors](https://effect.plants.sh/error-management/tagged-errors/).
- Recover from them with [Catching Errors](https://effect.plants.sh/error-management/catching-errors/).
- Inspect the full `Cause` — failures *and* defects — with
  [Result & Exit](https://effect.plants.sh/error-management/result-and-exit/).