http_gun/error

Describes why an HTTP request, a body read or a scripted exchange failed.

A Failure is opaque. Branch on its closed Kind with kind, and ask is_retryable whether sending the request again is safe:

case http_gun.send(client, req) {
  Ok(buffered) -> handle(buffered.response)
  Error(failure) ->
    case error.is_retryable(failure, idempotent: False) {
      True -> retry_later()
      False -> give_up(error.describe(failure))
    }
}

Every failure carries submission Evidence: NotSent when the request never reached the network, MaybeSent when the server may have received it. Neither proves the server acted. status returns the response status when final headers had arrived before the failure, for example when a body exceeded its limit, and headers returns that response’s headers, so a caller can still read retry-after from a 429 whose body was too large.

Reason gives detail and may gain variants in a minor release; match it with a _ arm, and branch on Kind instead where you can. name is a stable identifier for logs and stored records, and to_json with decoder round-trips a failure. A failure never holds a URL, header, query or body from the request; the response headers it may hold have the client’s redaction applied (config.with_redaction).

Types

Whether the request may have reached the server. A failure never reports a completed exchange: a completed exchange is a response.

pub type Evidence {
  NotSent
  MaybeSent
}

Constructors

  • NotSent

    The request was refused or failed before any byte reached the network. Sending it again cannot duplicate an effect.

  • MaybeSent

    The request may have reached the server, which may have acted on it.

A failed operation: its reason, submission evidence and, when final headers had arrived, the response status and headers.

pub opaque type Failure

The closed classification of a failure. It never gains variants, so an exhaustive match on it keeps compiling across minor releases.

pub type Kind {
  InvalidInput
  Refused
  Unavailable
  Network
  TimedOut
  TooLarge
  CancelledLocally
  Misuse
  Playback
}

Constructors

  • InvalidInput

    The request or a per-call setting is malformed. Fix the caller.

  • Refused

    The destination policy refused the host or a resolved address, or the client requires a view destination and the view set none.

  • Unavailable

    The client is stopped, restarting or full, it timed out waiting for a connection from its pool, or its recording closed.

  • Network

    Name resolution, connecting, TLS or the exchange itself failed.

  • TimedOut

    A connect, request or idle timeout expired.

  • TooLarge

    A size limit was exceeded.

  • CancelledLocally

    The caller cancelled the request or closed its body.

  • Misuse

    The body was read concurrently or from a process that does not own it.

  • Playback

    A scripted or cassette client found no matching exchange.

The category and unit of a size limit. May gain variants in a minor release.

pub type LimitKind {
  RequestBodyBytes
  RequestHeaderBytes
  RequestHeaderCount
  ResponseHeaderBytes
  ResponseHeaderCount
  BufferedBytes
  ResponseBodyBytes
  BatchBytes
}

Constructors

  • RequestBodyBytes
  • RequestHeaderBytes
  • RequestHeaderCount
  • ResponseHeaderBytes
  • ResponseHeaderCount
  • BufferedBytes

    Response bytes buffered between the network and the reader (config.with_max_buffered_bytes).

  • ResponseBodyBytes

    Body bytes collected by send or batch (config.with_max_response_body_bytes, http_gun.with_body_limit).

  • BatchBytes

    Body bytes retained by one batch call (config.with_max_batch_bytes).

Why a request failed, in detail. May gain variants in a minor release: match it with a _ arm, or branch on kind.

pub type Reason {
  InvalidRequest(RequestProblem)
  ClientClosed
  AdmissionFull
  PoolTimeout
  DestinationRejected(destination.Rejection)
  ViewDestinationRequired
  ResolutionFailed
  ConnectTimeout
  ConnectionFailed(TransportCause)
  RequestFailed(TransportCause)
  DeadlineExceeded
  IdleTimeout
  ReadConflict
  WrongOwner
  Closed
  Cancelled
  LimitExceeded(kind: LimitKind, limit: Int, observed: Int)
  PlaybackMismatch(position: Int)
  PlaybackExhausted
  RecordingClosed
}

Constructors

  • InvalidRequest(RequestProblem)
  • ClientClosed

    The client is stopped, or restarting under its supervisor.

  • AdmissionFull

    The client’s queue of waiting requests is full.

  • PoolTimeout

    No connection became available within the pool timeout.

  • DestinationRejected(destination.Rejection)
  • ViewDestinationRequired

    The client was configured with config.require_view_destination, and the request’s view chose no destination with http_gun.with_destination: no policy set a host list or refused an address class the client admits.

  • ResolutionFailed
  • ConnectTimeout

    Resolving and connecting did not finish within the connect timeout.

  • ConnectionFailed(TransportCause)
  • RequestFailed(TransportCause)
  • DeadlineExceeded

    The request timeout or the per-call deadline expired.

  • IdleTimeout

    No bytes arrived within the idle timeout while a read was waiting.

  • ReadConflict
  • WrongOwner
  • Closed

    The body was closed, or its owner or client stopped.

  • Cancelled
  • LimitExceeded(kind: LimitKind, limit: Int, observed: Int)
  • PlaybackMismatch(position: Int)

    The request did not match the scripted exchange at this position, counted from 0. The exchange stays in place.

  • PlaybackExhausted

    Every scripted exchange has been used.

  • RecordingClosed

    The recording behind a recording client is finishing or finished.

What was wrong with a request or a per-call setting. May gain variants in a minor release.

pub type RequestProblem {
  BodyNotBytes
  InvalidMethod
  InvalidOrigin
  InvalidTarget
  InvalidHeader
  InvalidBodyLimit
  InvalidBatch
}

Constructors

  • BodyNotBytes

    The body is a bit array whose size is not a whole number of bytes.

  • InvalidMethod

    The method is not an HTTP token.

  • InvalidOrigin

    The host is empty or malformed, or the port is outside 1..65535.

  • InvalidTarget

    The path or query contains whitespace or control characters, or the path does not start with /.

  • InvalidHeader

    A header name is not a lowercase token, or a value contains CR, LF or NUL.

  • InvalidBodyLimit

    A body limit set with http_gun.with_body_limit is negative.

  • InvalidBatch

    A batch needs 1 to 1,024 workers and at most 10,000 requests.

A stable category of transport error. UnknownTransport keeps the uncertainty; no dependency term or server-supplied text is exposed. May gain variants in a minor release.

pub type TransportCause {
  NameResolutionFailed
  ConnectionRefused
  CertificateRejected
  TlsFailed
  ConnectionReset
  PeerClosed
  PeerDraining
  ProtocolError
  HeaderLimitReached
  TransportTimeout
  UnexpectedProtocol
  UnknownTransport
}

Constructors

  • NameResolutionFailed
  • ConnectionRefused
  • CertificateRejected
  • TlsFailed
  • ConnectionReset
  • PeerClosed
  • PeerDraining
  • ProtocolError
  • HeaderLimitReached

    Gun refused a response header or trailer list over its count limit before delivering it. Gun does not report the measured value.

  • TransportTimeout
  • UnexpectedProtocol
  • UnknownTransport

Values

pub fn decoder() -> decode.Decoder(Failure)

Decode the JSON that to_json writes. An unknown name fails to decode.

pub fn describe(failure: Failure) -> String

A one-line description for logs and user interfaces. It never contains a URL, header, query or body.

pub fn evidence(failure: Failure) -> Evidence
pub fn headers(failure: Failure) -> List(#(String, String))

The response headers, in arrival order with duplicates kept, when final headers had arrived before a send or batch failure; otherwise []. The client’s redaction (config.with_redaction) has removed its listed headers, the credential headers by default. A streaming caller already holds the headers in its Response.

// A 429 whose body exceeded `with_body_limit(_, _, Fail)`.
list.key_find(error.headers(failure), "retry-after")
pub fn is_retryable(
  failure: Failure,
  idempotent idempotent: Bool,
) -> Bool

Whether sending the same request again is safe and may succeed.

A NotSent failure of kind Unavailable, Network or TimedOut is retryable. A MaybeSent failure of those kinds is retryable only for an idempotent request, because the server may already have acted on it. No other kind is retryable. HTTP Gun itself never retries.

pub fn kind(failure: Failure) -> Kind

The closed classification of the failure.

pub fn name(failure: Failure) -> String

A stable identifier for logs, metrics and stored records, such as "connection_failed.connection_refused" or "limit_exceeded.response_body_bytes". decoder reads it back.

pub fn new(reason: Reason, evidence: Evidence) -> Failure

Build a failure, for scripted replies and test doubles.

pub fn reason(failure: Failure) -> Reason
pub fn status(failure: Failure) -> option.Option(Int)

The response status, when final headers had arrived before the failure. A body over its limit fails with the status of the response it belonged to.

pub fn to_json(failure: Failure) -> json.Json

Encode a failure as JSON, for stored records such as a durable step error. decoder reads it back to an equal failure. Response headers, when present, are stored as [name, value] pairs, after the client’s redaction.

{"name": "limit_exceeded.response_body_bytes", "evidence": "maybe_sent",
 "status": 429, "headers": [["retry-after", "30"]],
 "limit": 8388608, "observed": 8388609}
pub fn with_headers(
  failure: Failure,
  headers: List(#(String, String)),
) -> Failure

Record the response headers that had arrived before the failure, replacing any recorded before. The caller applies its own redaction.

pub fn with_status(failure: Failure, status: Int) -> Failure

Record the response status that had arrived before the failure.

✨ Search Document