---
title: "Fallback chain"
manual: "TYPO3 LLM Extension"
version: "0.35"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-llm:developer-fallback-chain@0.35"
source: "Developer/FallbackChain.rst"
modified: "2026-09-16T22:09:16+00:00"
---

# Fallback chain

A `LlmConfiguration` can carry an ordered list of other
configuration identifiers to fall back to on *retryable* provider
failures. The lookup happens transparently inside
`NetresearchNrLlmServiceLlmServiceManager::chatWithConfiguration()`
and `completeWithConfiguration()`. Callers see a regular
completion response or a typed exception; they never need to
reach into retry mechanics.

## Configuring a chain

The `tx_nrllm_configuration.fallback_chain` column stores a
JSON **object** with a single key, `configurationIdentifiers`, whose
value is the ordered array of target configuration identifiers:

**Example payload stored in `fallback_chain`**

```json
{"configurationIdentifiers": ["claude-sonnet", "ollama-local"]}
```

Editors paste that JSON into the **Fallback Chain** tab in
the backend form. The order is the retry order. Identifiers are
matched case-insensitively against `tx_nrllm_configuration.identifier`.
Using an object (rather than a bare top-level array) leaves room for
future sibling fields — e.g. per-link retry policy — without a
schema break.

## Retryable vs. non-retryable errors

Fallback only triggers for errors the next provider might actually
recover from:

| Exception | Retryable? |
| --- | --- |
| `ProviderConnectionException` (network, timeout, HTTP 5xx, retries exhausted) | Yes |
| `ProviderResponseException` with code `429` (rate-limited by this provider) | Yes |
| `ProviderResponseException` with any other 4xx (authentication, bad request, not found, …) | No. Bubbles up. A different provider with the same input would fail the same way. |
| `ProviderConfigurationException` | No. Misconfiguration is a human problem. |
| `UnsupportedFeatureException` | No. Fallback won't make a text-only provider handle images. |

When every configuration in the chain trips a retryable error,
`NetresearchNrLlmProviderExceptionFallbackChainExhaustedException`
is thrown. It carries the per-attempt errors so consumers can
surface the full failure sequence.

## Scope limits

v1 is deliberately narrow:

-   **No streaming.** `streamChatWithConfiguration()` does not
    wrap the call. Once the first chunk has been yielded to the
    caller, mid-stream provider-switching would be detectable and
    surprising.
-   **No recursion.** A fallback configuration's own chain is
    ignored. This avoids cycles (`a -> b -> a`) and unbounded
    attempt trees.
-   **Single primary-only chain is a no-op.** If the configured
    chain contains only the primary's own identifier, the primary's
    original exception is rethrown verbatim rather than wrapped in
    `FallbackChainExhaustedException`.

## Using the DTO directly

For programmatic construction — e.g. a wizard that generates a
configuration and also sets up fallback — use the
`NetresearchNrLlmDomainDTOFallbackChain` value object:

**EXT:my_ext/Classes/Service/Setup.php**

```php
use Netresearch\NrLlm\Domain\DTO\FallbackChain;

$chain = (new FallbackChain())
    ->withLink('claude-sonnet')
    ->withLink('ollama-local');

$configuration->setFallbackChainDTO($chain);
```

The DTO trims and lowercases identifiers on entry, deduplicates
them, and silently rejects empty strings and non-string entries
read from malformed JSON. See [ADR-021: Provider Fallback Chain](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-021@0.35) for the full design
rationale and the alternatives we ruled out.
