<!-- Markdown mirror. Canonical: https://empryo.com/docs/recipes/model-fallback -->

# Model fallback chains

> Give each model a list of backups so Empryo switches to another model when a provider is overloaded, rate-limited, or timing out, and your session keeps going.

When a provider is overloaded, rate-limits you, or returns server errors, Empryo can switch to a backup model and keep working. You set the backups per model: each model you use can have its own ordered list.

## Set it up in the app

In the terminal app, open the router:

```
/router
```

Scroll to **Model Fallback**. Each model you use, your default model plus every model in the [task router](/docs/recipes/task-router), has its own row. Press <kbd>Enter</kbd> to add a backup, or <kbd>d</kbd> to clear the list.

In the desktop app, the backup lists are in **Settings → Agent behavior**. Changes save immediately.

## Or edit the config

```json
{
  "defaultModel": "anthropic/claude-sonnet-4-5",
  "modelFallback": {
    "anthropic/claude-sonnet-4-5": [
      "openrouter/anthropic/claude-sonnet-4.5",
      "openai/gpt-5"
    ],
    "anthropic/claude-haiku-4-5": [
      "google/gemini-2.5-flash"
    ]
  }
}
```

Each key is a model id. Its value is the list of backups, tried in order.

## What happens when a model fails

1. **Retry first.** Empryo retries the same model a few times (3 by default), waiting a little longer each time.
2. **Switch.** If it still fails, Empryo moves to the next model in the list. A message in the chat says `Switched to fallback model: …`, and the header shows the new model.
3. **Start over.** If every backup fails, Empryo goes back to the first model and tries the whole list again, up to 3 rounds. After that it shows the original error.
4. **You can always stop it.** `Ctrl+X` stops immediately, wherever it is in the list.

Models that have failed recently are tried later, so a provider that is down does not slow down every request.

## Which errors trigger a switch

**Temporary errors** trigger a retry and then a switch:

- HTTP `408`, `429`, and any `5xx`, including overload errors such as `529`.
- Quota messages that come without a status code, such as "credit balance is exhausted".
- Network problems: timeouts, refused or reset connections, DNS failures, and streams that close early.

**Permanent errors** are shown right away, with no retry:

- `401`, and `403` unless the message is about rate limits or overload. These mean the key or account is wrong.
- Other `4xx` errors, such as `400` (bad request), `404` (unknown model), or `422`. These mean the request itself is wrong, so another model would not help.

## Tune the retries

```json
{
  "retry": {
    "maxTransientRetries": 3,
    "maxStallRetries": 3,
    "baseDelayMs": 1000
  }
}
```

- `maxTransientRetries`: retries on the same model before switching. 1 to 10.
- `maxStallRetries`: retries when a response stops arriving. Only used when the stall watchdog is on (`/timeouts`). 1 to 10.
- `baseDelayMs`: how long to wait before the first retry. The wait doubles each time, with some randomness. 250 to 60000 ms.

The older `maxAttempts` field still works as a default for both retry counts. You can also change these with `/retry`.

## Good uses

- **Overloaded provider.** When Claude returns `529 overloaded`, fall back to the same model through a gateway such as `openrouter/…` or `llmgateway/…`.
- **Separate quotas.** Pair a direct Anthropic key with a gateway account, so hitting one limit does not stop you.
- **Outages.** Mix providers (`anthropic/…`, `openai/…`, `google/…`) so one provider's outage does not end your session.

## What it does not do

- **Switch to save money.** Fallback reacts to failures, not cost. Use the [task router](/docs/recipes/task-router) to send work to cheaper models.
- **Handle stalls.** If a response just stops without an error, the stall watchdog retries on the same model. Fallback only reacts to errors.
- **Retry permanent errors.** A bad key, a malformed request, or an unknown model is shown right away.
