# `ExternalService.RetriesExhausted`
[🔗](https://github.com/jvoegele/external_service/blob/main/lib/external_service/errors.ex#L1)

Raised or returned when the allowable number of retries (or the retry time
budget) is exceeded while calling an external service.

This is an [Errata](https://hexdocs.pm/errata) infrastructure error. The same
value is returned in an `{:error, error}` tuple by `ExternalService.call/3` and
raised by `ExternalService.call!/3`.

Its `:context` contains:

  * `:service` — the fuse name the error relates to.
  * `:reason` — the retry reason: the value from the function's `{:retry, reason}`
    return, or `:reason_unknown` when the function returned a bare `:retry`.

When that reason is an exception — including any Errata error — it is also set
as this error's `:cause`, so `Errata.root_cause/1` reaches the underlying
failure and `Errata.format_chain/1` prints the chain.

## Retryability

Unlike the other errors here, this one is **not** retryable: it exists to say
that retrying has already been tried and did not work. Note that Errata's
classification has no notion of *when* — "not retryable" here means "not worth
retrying now". Re-attempting the work at a coarser layer, such as a background
job re-enqueuing itself minutes later, is a perfectly reasonable response to
this error; it just isn't the question `Errata.retryable?/1` is answering.

# `t`

```elixir
@type t() :: Errata.infrastructure_error()
```

# `code`

```elixir
@spec code(Errata.error()) :: String.t() | nil
```

Returns the stable external code for this error, or `nil` if it has none.

No code is set for this error type. Set one with the `:code` option, or
override this function to derive a code from the error's `:reason` or
`:context`. See also `Errata.code/1`.

# `display_message`

```elixir
@spec display_message(Errata.error()) :: String.t() | nil
```

Returns the user-facing _display message_ for this error (the `:message` field by default).

This is distinct from `Exception.message/1`, which also includes the error's `:reason` and is
aimed at developers. Override this function to compute a message from the error's `:reason` or
`:context`:

    def display_message(%{context: %{order_id: id}}), do: "order #{id} does not exist"
    def display_message(error), do: error.message

`Errata.display_message/1` and `Errata.to_map/1` both dispatch through this function, so an
override applies to the JSON encoding and to anything rendering the error for a user. See also
`Errata.display_message/1`.

# `http_status`

```elixir
@spec http_status(Errata.error()) :: non_neg_integer()
```

Returns the HTTP status code associated with this error (`503` by default).

The default is derived from the error's kind, or set via the `:http_status`
option. Override this function to compute a status from the error's `:reason`
or `:context`. See also `Errata.http_status/1`.

# `redact_context`

```elixir
@spec redact_context(Errata.error()) :: map()
```

Returns this error's `:context` with sensitive values redacted (no keys declared).

Called wherever Errata serializes the context — `to_map/1` and the JSON
encoding, `Errata.log/2` metadata, and `Errata.report/2` telemetry metadata.
The error struct itself is left alone, so the real values remain available
locally for debugging.

Declared keys are redacted recursively and match whether written as atoms or
binaries. Set them with the `:redact` option, add a global floor with
`config :errata, redact: [...]`, or override this function for full control.
See `Errata.Redaction`.

# `retryable?`

```elixir
@spec retryable?(Errata.error()) :: boolean()
```

Returns whether this error is considered retryable (`false` by default).

The default is derived from the error's kind — `:infrastructure` errors are
retryable, `:domain` and `:general` errors are not — or set via the
`:retryable` option. Override this function to decide from the error's
`:reason` or `:context`. See also `Errata.retryable?/1`.

# `severity`

```elixir
@spec severity(Errata.error()) :: Logger.level()
```

Returns the severity of this error (`:error` by default).

The severity is a `t:Logger.level/0` and is the level at which `Errata.log/2`
logs the error when no level is given explicitly. Set it with the `:severity`
option, or override this function to compute a severity from the error's
`:reason` or `:context`. See also `Errata.severity/1`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
