# `GcpCompute.Error`
[🔗](https://github.com/nyo16/gcp_compute/blob/v0.3.0/lib/gcp_compute/error.ex#L1)

Normalized error returned by every `GcpCompute` function.

Every public function returns `{:ok, result}` or `{:error, %GcpCompute.Error{}}`
— the config builders (`GcpCompute.Config.new/1` and friends) and the spec
builder (`GcpCompute.Instance.spec/1`) included, so a single `with` can thread
config construction and API calls through one error clause.

The struct is also an exception, so `Exception.message/1` is the
human-readable accessor and it can be re-raised with `raise/1`. The bang
variants (`GcpCompute.Config.new!/1`, `GcpCompute.Config.local!/1`,
`GcpCompute.Instance.spec!/1`) raise `ArgumentError` carrying that message —
they never raise this struct.

## Fields

  * `:reason` — a coarse, matchable atom (see below).
  * `:status` — HTTP status code, when the error came from an API response.
  * `:message` — human readable message (from GCP when available). It describes
    the *shape* of the problem and never interpolates the offending value; raw
    values live in `:body`.
  * `:errors` — the raw `error.errors` list returned by the Compute API.
  * `:body` — the underlying body/exception, for debugging. **Redacted by this
    module's `Inspect` implementation** — it can carry a bearer token.
  * `:operation` — the failed `GcpCompute.Operation`, for operation errors.

## Reasons

Request / response:

  * `:api_error` — non-2xx response from the Compute API.
  * `:invalid_response` — a 2xx response whose body is not the expected shape
    (not a JSON object, an operation with no `name`, …).
  * `:transport` — connection or socket level failure (`Req`/`Mint` exception).
  * `:timeout` — `GcpCompute.Operations.poll_until_done/3`'s wall-clock budget
    expired.
  * `:operation_failed` — the operation reached `DONE` carrying an error payload.

Authentication:

  * `:token_fetch_failed` — the token provider returned `{:error, reason}`.
  * `:missing_token` — the provider returned a token map with no usable token.
  * `:invalid_token_provider_arg` — the provider's `arg` had the wrong shape.
  * `:invalid_token_provider_return` — the provider returned something that is
    neither `{:ok, token_map}` nor `{:error, reason}`.
  * `:goth_not_available` — Goth is not loaded.
  * `:goth_not_running` — no Goth server is registered under the configured name.
  * `:goth_fetch_timeout` — the call into Goth timed out.

Caller input:

  * `:invalid_config` — `GcpCompute.Config` option validation failed.
  * `:invalid_spec` — `GcpCompute.Instance.spec/1` validation failed, or an
    instance body that is neither a keyword list nor a plain map was passed.
  * `:invalid_name` — a resource name that cannot be used as a URL path segment
    (`""`, `"."`, `".."`).
  * `:invalid_argument` — an unknown or malformed call option.
  * `:missing_instance_name` — `insert_and_wait/3` found no name in the spec.

# `t`

```elixir
@type t() :: %GcpCompute.Error{
  __exception__: true,
  body: term(),
  errors: list() | nil,
  message: String.t() | nil,
  operation: GcpCompute.Operation.t() | nil,
  reason: atom(),
  status: non_neg_integer() | nil
}
```

# `from_exception`

```elixir
@spec from_exception(Exception.t()) :: t()
```

Build a transport-level error from a `Req`/`Mint` exception.

This is the one place `:message` carries text this library did not write —
`Exception.message/1` on a transport exception is a fixed reason string
(`"connection refused"`, `"timeout"`) and is the only useful thing to show.
The exception itself, whose request headers can carry the bearer token, goes
in `:body`, which `Inspect` redacts.

# `from_response`

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

Build an error from a non-2xx Compute API response.

Handles the standard Google error envelope:

    %{"error" => %{"code" => 403, "message" => "...", "errors" => [...]}}

---

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