Tango.RefreshError (tango v0.3.0)

Copy Markdown View Source

Closed refresh-failure values for token refresh.

HTTP status wins over a misleading OAuth error body on 429, 408, and 5xx. Only the fixed OAuth error allowlist becomes an atom. Storage strings are safe JSON scalars; unknown terms become "unknown_refresh_error".

Summary

Functions

Scheduling delay in seconds for a refresh failure.

Classifies a refresh failure for the retry policy.

Classifies an HTTP token-endpoint failure.

Bounds a transport failure to :timeout or :network_error.

Serializes a refresh failure to an allowlisted string.

Types

class()

@type class() :: :credential | :client | :request | :transient | :local

oauth_code()

@type oauth_code() ::
  :invalid_grant
  | :invalid_client
  | :unauthorized_client
  | :invalid_request
  | :invalid_scope
  | :unsupported_grant_type
  | :temporarily_unavailable
  | :server_error

reason()

@type reason() ::
  {:rate_limited, non_neg_integer() | nil}
  | {:http_error, integer()}
  | {:oauth_error, oauth_code()}
  | {:transport_error, :timeout | :network_error}
  | :no_refresh_token
  | :refresh_not_allowed
  | :provider_not_found
  | :invalid_config
  | :persistence_failed
  | :transaction_failed

Functions

backoff_seconds(arg1)

@spec backoff_seconds(reason()) :: non_neg_integer()

Scheduling delay in seconds for a refresh failure.

A numeric Retry-After is capped at 3600. Zero is kept. Every other reason waits 300 seconds. The reason itself keeps the uncapped parsed value.

classify(reason)

@spec classify(term()) :: class()

Classifies a refresh failure for the retry policy.

Unknown terms are :local and are not treated as invalid credentials.

from_response(status, headers, body)

@spec from_response(integer(), [{String.t(), String.t()}], term()) :: reason()

Classifies an HTTP token-endpoint failure.

headers is a tuple list. The first retry-after value is kept only when the whole value is a non-negative decimal integer. A 429, 408, or 5xx status ignores the body. Other 4xx failures use an allowlisted "error" code when the body is a map or a JSON object; everything else stays {:http_error, status}.

from_transport(reason)

@spec from_transport(term()) :: {:transport_error, :timeout | :network_error}

Bounds a transport failure to :timeout or :network_error.

:timeout and :etimedout are timeouts. Every other term, including unknown maps and strings, is a network error.

to_storage(reason)

@spec to_storage(term()) :: String.t()

Serializes a refresh failure to an allowlisted string.

Unknown terms become "unknown_refresh_error". The result is a JSON scalar and never contains a response body, token, or arbitrary term.