Zizq.Error exception (Zizq v0.6.0)

Copy Markdown View Source

The error returned by every Zizq function.

Every failure arrives as {:error, %Zizq.Error{}} — a request the server rejected, a server that could not be reached, a body that would not encode — so there is a single shape to match on wherever the problem occurred. The :reason field says which kind it was.

case Zizq.enqueue(job, MyApp.Zizq) do
  {:ok, job} ->
    job

  {:error, %Zizq.Error{reason: :forbidden}} ->
    needs_a_pro_license()

  {:error, %Zizq.Error{} = error} ->
    Logger.error(Exception.message(error))
end

Reasons are atoms, so a guard covers a whole class of failure when the specific one does not matter:

{:error, %Zizq.Error{reason: reason}} when reason in [:not_found, :conflict] ->
  :already_handled

Fields

  • :reason — the kind of failure; see reason/0
  • :message — human-readable, preferring the server's own wording
  • :status — the HTTP status, or nil when no response arrived
  • :body — the decoded response body, when there was one
  • :cause — the underlying exception behind a transport or codec failure, so the original detail is still available

Deciding whether to retry

Use retryable?/1 rather than inspecting the status yourself. It keeps the policy in one place, and it is the same policy the worker applies to acknowledgements: transport failures and 5xx are transient, everything else is permanent.

Summary

Types

Why the call failed.

t()

Functions

Wrap a response body that could not be deserialised.

Wrap a request body that could not be serialised.

Build an error from a non-success HTTP response.

Whether retrying the same request could plausibly succeed.

Wrap a transport-level failure.

Types

reason()

@type reason() ::
  :not_found
  | :conflict
  | :forbidden
  | :unsupported_format
  | :invalid_request
  | :client_error
  | :server_error
  | :transport
  | :encode
  | :decode
  | :unexpected_status

Why the call failed.

  • :not_found — 404. A job or cron entry that does not exist.
  • :conflict — 409.
  • :forbidden — 403. The server refused a licensed feature; see the message for which one.
  • :unsupported_format — 406 or 415. Content negotiation failed, which indicates a client bug rather than bad input.
  • :invalid_request — 400 or 422. The server rejected the request.
  • :client_error — any other 4xx.
  • :server_error — any 5xx.
  • :transport — the request never completed: connection refused, timeout, DNS failure, pool unavailable.
  • :encode — the request body could not be serialised.
  • :decode — the response body could not be deserialised.
  • :unexpected_status — the server answered with a status this endpoint does not know how to interpret. A client bug, or a server newer than this client.

t()

@type t() :: %Zizq.Error{
  __exception__: term(),
  body: term(),
  cause: Exception.t() | nil,
  message: String.t(),
  reason: reason(),
  status: non_neg_integer() | nil
}

Functions

decode(cause)

@spec decode(Exception.t()) :: t()

Wrap a response body that could not be deserialised.

encode(cause)

@spec encode(Exception.t()) :: t()

Wrap a request body that could not be serialised.

from_response(status, body)

@spec from_response(non_neg_integer(), term()) :: t()

Build an error from a non-success HTTP response.

Callers decide which statuses count as failures; the transport layer passes every status through untouched. That matters because status alone is not always enough: a 422 from the bulk acknowledge endpoint reports partial success ({"not_found" => [...]}, the rest having completed), where a 422 elsewhere is a genuine rejection.

retryable?(error)

@spec retryable?(t()) :: boolean()

Whether retrying the same request could plausibly succeed.

True for :transport and :server_error; false for everything else. A 4xx will fail identically however many times it is sent, and a body that would not encode will not encode on a second attempt.

iex> Zizq.Error.retryable?(%Zizq.Error{reason: :server_error})
true

iex> Zizq.Error.retryable?(%Zizq.Error{reason: :not_found})
false

transport(cause)

@spec transport(Exception.t()) :: t()

Wrap a transport-level failure.