---
title: "Error Model"
url: "https://effect-auth.itsbroly.com/concepts/error-model/"
description: "Understand expected failures, continuations, defects, and error mapping across effect-auth boundaries."
---



effect-auth keeps failures typed at the layer that can act on them, then deliberately narrows what crosses a public boundary. A storage failure is useful to server code; exposing its cause to a browser is not. Conversely, invalid credentials and rate limiting are expected outcomes that clients must be able to handle.

```text
domain service       HTTP operation       HttpApi boundary       browser client
tagged errors   -->  map and sanitize --> declared schema   --> decoded rejection
AuthResult value --> commit/encode    --> success schema    --> continuation value
defect          -------------------------------------------> server failure/telemetry
```

This is the same layering described in [Architecture](/concepts/architecture/): domain primitives do not define an HTTP contract, HTTP operations add transport semantics, `HttpApi` encodes that contract, and the client decodes its public form.

## Three kinds of outcome [#three-kinds-of-outcome]

| Kind                    | Examples                                                          | Representation                                   | Consumer action                                        |
| ----------------------- | ----------------------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------ |
| Expected failure        | invalid credentials, unauthenticated, policy denied, rate limited | Typed Effect error, then a declared HTTP error   | Branch, retry, or display a safe message               |
| Successful continuation | MFA, email verification, login approval, passkey enrollment       | `AuthResult` success value                       | Preserve the flow data and continue authentication     |
| Defect                  | violated invariant, unexpected thrown exception, programmer bug   | Effect defect/cause, not the typed error channel | Log, trace, alert, and return a generic server failure |

Do not turn every non-authenticated result into an error. `AuthResult.RequiresMfa`, `RequiresEmailVerification`, `RequiresLoginApproval`, and `RequiresPasskeyEnrollment` mean the primary operation succeeded but the authentication flow is incomplete. They carry values such as `flowId` needed by the next endpoint. The HTTP layer encodes these as successful response variants, and UI code should switch on the decoded `type`. See [Password authentication](/authentication/password/#handle-authentication-continuations) for the complete browser pattern.

## Domain errors [#domain-errors]

Primitives use tagged errors in the Effect error channel. Examples include `StorageError`, `PasswordHashError`, and `AuthFlowStateError`. Tags allow exhaustive, local handling without parsing messages:

```ts
password.signIn(input).pipe(
  Effect.catchTag("StorageError", () => Effect.fail(AppAuthUnavailable()))
)
```

At this level, your application owns the mapping. A typed error is an anticipated failure mode, not necessarily safe public information. `StorageError` may retain an entity, operation, message, and underlying cause for diagnostics. It should normally remain in server logs or telemetry.

Defects are different. They are absent from `Effect<A, E, R>`'s `E` type and indicate behavior the normal contract did not promise. Do not recover from all causes merely to present their text to a caller. Observe defects at the server boundary, preserve their cause for operators, and emit a generic response. Storage adapters should convert anticipated driver failures into `StorageError`; truly unexpected bugs may still defect.

## HTTP operation mapping [#http-operation-mapping]

`*HttpOperations` are the standard mapping seam between domain and HTTP. They apply operation security, translate domain results and errors, and commit session cookies. For example, password sign-up maps `IdentityAlreadyRegisteredError` to the public 409 `identity_already_registered`, while storage, cryptography, hashing, and session-creation failures become `AuthInternalError` with status 500. Password sign-in maps credential outcomes without revealing whether an identity exists.

The public union uses schema-backed tagged classes such as `AuthBadRequestError`, `AuthUnauthenticatedError`, `AuthPolicyDeniedError`, and `AuthRateLimitedError`. Each has a stable `code`, safe `message`, and HTTP status; only errors declared by an endpoint belong in its encoded contract. Reusing an HTTP operation in a [Custom Auth API](/guides/custom-auth-api/) preserves its mapping, but your endpoint must declare compatible success and error schemas.

Public messages are an information-disclosure boundary, not debug output. Avoid forwarding database errors, provider payloads, stack traces, secrets, account existence, or policy internals. Log the original cause with request correlation, then return the least specific message the client needs. This is especially important for sign-in, password reset, and account-discovery paths.

## Schema failures [#schema-failures]

Request decoding happens before a handler. The standard API applies `AuthSchemaErrorMiddleware`, whose live layer converts schema failures into `AuthBadRequestError`: `code: "bad_request"`, message `"Invalid request"`, and at most ten normalized issues containing a path and a coarse code such as `required`, `invalid_type`, or `unexpected_key`.

When assembling a custom `HttpApi`, attach `AuthSchemaErrorMiddleware` to the API or group and provide `AuthSchemaErrorMiddlewareLive`. Without it, decoding failures follow Effect's default schema-error behavior rather than effect-auth's public error shape. If you design a different public schema-error contract, define and map it explicitly, update the endpoint error schemas, and provide a matching custom client extension. The standard client cannot infer an application-owned API.

## Browser handling [#browser-handling]

The [Browser Client](/clients/browser-client/) rejects failed HTTP calls with decoded public error objects. `isAuthApiError` narrows the standard union, `authClientErrorStatus` recovers its mapped status, and `authClientErrorMessage` supplies a displayable fallback:

```ts
try {
  return await auth.password.signIn({
    identity: { scope: { type: "global" }, kind: "email", value: email },
    password,
  });
} catch (error) {
  const message = isAuthApiError(error)
    ? authClientErrorMessage(error)
    : "Authentication is temporarily unavailable";
  showError(message);
}
```

Keep the unknown branch: network failures, cancellation, browser API failures, and response-decode failures are not `AuthHttpError`. Helpers do not make arbitrary error text safe; use `authClientErrorMessage` for decoded public errors and a controlled fallback otherwise. For contract ownership and extension points, continue with [Custom Auth API](/guides/custom-auth-api/).

