> ## Documentation Index
> Fetch the complete documentation index at: https://mifr.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors and headers

> Status codes, mifr_action values, and x-mifr-* verdict headers.

Errors use the OpenAI envelope plus `code` and `mifr_action` (the SDK ignores those extras). SSE uses the same object in `data: {"error":…}` and still omits `[DONE]`. An HTTP abort sends no body.

```json theme={null}
{
  "error": {
    "message": "…",
    "type": "api_error",
    "code": "provider_unavailable",
    "param": null,
    "mifr_action": "wait"
  }
}
```

`mifr_action` is one of `retry`, `wait`, `fix_key`, `fix_request`, or `pick_model`.

## Status codes

| HTTP | `code`                  | `mifr_action` | When                                                                       |
| ---- | ----------------------- | ------------- | -------------------------------------------------------------------------- |
| 401  | `invalid_api_key`       | `fix_key`     | Missing or wrong Bearer                                                    |
| 400  | `invalid_request`       | `fix_request` | Bad JSON, missing `model`/`messages`, unsupported part, oversized messages |
| 413  | `invalid_request`       | `fix_request` | Body over 1 MiB                                                            |
| 405  | `invalid_request`       | `fix_request` | Method not allowed                                                         |
| 404  | `invalid_request`       | `fix_request` | No such route                                                              |
| 404  | `model_not_found`       | `pick_model`  | No provider serving that model at the floor, or unknown alias              |
| 503  | `provider_unavailable`  | `wait`        | None at the floor, or a named Mac offline / below floor                    |
| 503  | `directory_unavailable` | `wait`        | Chat key lookup down                                                       |
| 504  | `timeout`               | `retry`       | Overall wait exceeded                                                      |
| 502  | `trust_refused`         | `wait`        | Log / MDA / pin after failover exhausted                                   |
| 502  | `receipt_failed`        | `retry`       | Disputed receipt                                                           |
| 502  | `provider_error`        | `retry`       | Transport                                                                  |
| 502  | `stream_interrupted`    | `retry`       | Post-token fail; new request, do not splice                                |
| 502  | `directory_unavailable` | `wait`        | `/v1/models` or `/v1/providers` could not list                             |

Unknown routes return 404. `PUT` / `DELETE` / `TRACE` return 405 with `Allow: GET, POST, OPTIONS`. If the caller hangs up, the gateway records `aborted` and sends no body.

## Verdict headers

Successful non-stream completions put identity on HTTP headers. On a stream, the same facts appear on the terminal SSE chunk with snake\_case names (`-` becomes `_`). They cannot ride on the SSE HTTP head, because that head is flushed before a provider is chosen.

Headers are the gateway's report; `mifr_receipt` is the proof.

| HTTP                 | SSE field            | When                                                          |
| -------------------- | -------------------- | ------------------------------------------------------------- |
| `x-mifr-trust`       | `x_mifr_trust`       | Always: granted rung (`hardware` / `transparent` / `process`) |
| `x-mifr-provider`    | `x_mifr_provider`    | Always: provider key id                                       |
| `x-mifr-model`       | `x_mifr_model`       | Always: catalog alias served                                  |
| `x-mifr-attempts`    | `x_mifr_attempts`    | Always: 1 means no hop                                        |
| `x-mifr-serial`      | `x_mifr_serial`      | `hardware`: full Apple-attested serial                        |
| `x-mifr-pick`        | `x_mifr_pick`        | Omitted when pinned: `idle` / `queue`                         |
| `x-mifr-chip`        | `x_mifr_chip`        | Omitted when pinned: `small` / `large` / `unknown`            |
| `x-mifr-cdhash`      | `x_mifr_cdhash`      | When the receipt carries a signed cdhash                      |
| `x-mifr-log-index`   | `x_mifr_log_index`   | When that cdhash is a logged build                            |
| `x-mifr-posture-age` | `x_mifr_posture_age` | `hardware`: SecurityInfo age in seconds                       |
| `x-mifr-mid-hop`     | `x_mifr_mid_hop`     | N1 died after a token and N2 finished                         |
| `x-mifr-hedge`       | `x_mifr_hedge`       | Pre-token idle-idle pair was dispatched (`1`)                 |
| `x-mifr-e2e-ms`      | `x_mifr_e2e_ms`      | Always on a finished call                                     |
| `x-mifr-ttft-ms`     | `x_mifr_ttft_ms`     | Stream only: first content chunk                              |
| `x-mifr-itl-ms`      | `x_mifr_itl_ms`      | Stream only: mean gap between non-empty chunks                |
| `x-mifr-tok-s`       | `x_mifr_tok_s`       | When receipt `completion_tokens > 0`                          |
| `x-mifr-tok-s-kind`  | `x_mifr_tok_s_kind`  | `decode` (stream) or `e2e` (non-stream)                       |

OpenAI and Vercel SDKs ignore extra HTTP headers and extra JSON keys.

## Routing

When you omit `provider`, the gateway picks idle-first near-tie. If the chosen Mac dies, hangs (90 seconds with no token), stalls (90 seconds since the last token), or returns a bad receipt, the gateway retries another qualified Mac: at most two pre-token failovers, plus one mid-stream hop after the first token. Two idle Macs may be raced before the first token (`x-mifr-hedge: 1`). It never retries the same id, never drops to a lower floor, and never returns an unverified completion. A pin does not hop or hedge.
