#21152: fix(agents): throw FailoverError for unknown model so fallback chain is not bypassed
agents
size: XS
Cluster:
Model Fallback and Error Handling
## Summary
Fixes #21107.
When `resolveModel()` cannot find a model in the catalog, it returns `{ error: string }`. The caller in `runEmbeddedPiAgent()` then does:
```ts
if (!model) {
throw new Error(error ?? `Unknown model: ${provider}/${modelId}`);
}
```
Because this is a plain `Error`, `coerceToFailoverError()` in `runWithModelFallback()` does not recognise the message pattern (it checks for rate-limit, billing, auth, timeout, and format strings — not "Unknown model:"). So the error is rethrown immediately, bypassing every fallback candidate the user configured.
## Fix
Change the single `throw` to use `FailoverError` (already imported on line 20):
```ts
if (!model) {
throw new FailoverError(
error ?? `Unknown model: ${provider}/${modelId}`,
{ reason: "format", provider, model: modelId },
);
}
```
`FailoverError` is already caught correctly by `runWithModelFallback()` — the fallback chain will now cascade through all configured fallback models as the user expects.
## Why `reason: "format"`
The model-not-found error is structurally analogous to an unsupported-model response (wrong model name / ID doesn't exist). Using `"format"` matches the existing convention used for similar pre-flight errors (see L301-L304 in this file).
## Test cases covered by existing tests
- Unknown primary model with valid fallbacks → fallback chain now triggered (was: crash)
- Unknown primary model with no fallbacks → plain error still surfaced (FailoverError is rethrown by `runWithModelFallback()` when no more candidates)
- Valid model → no behaviour change
## One-line diff
```diff
- throw new Error(error ?? `Unknown model: ${provider}/${modelId}`);
+ throw new FailoverError(
+ error ?? `Unknown model: ${provider}/${modelId}`,
+ { reason: "format", provider, model: modelId },
+ );
```
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Changed error handling for unknown models from plain `Error` to `FailoverError` with `reason: "format"`, enabling the fallback chain to engage when `resolveModel()` cannot find a model in the catalog.
### Changes
- Modified `src/agents/pi-embedded-runner/run.ts:277-280` to throw `FailoverError` instead of plain `Error` when model resolution fails
- Added `reason: "format"` metadata to classify the error for fallback handling
### Why this matters
Previously, when a model was not found in the catalog, the plain `Error` would bypass `runWithModelFallback()`'s fallback chain because `coerceToFailoverError()` couldn't recognize the "Unknown model:" pattern. Now the fallback chain will correctly cascade through configured fallback models.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The change is a focused, single-line fix that correctly uses an existing error class (`FailoverError`) that's already imported and used elsewhere in the file. The `reason: "format"` choice is consistent with the context window guard error on line 304-307 and matches the semantic intent of "unsupported model format". The fallback mechanism in `model-fallback.ts:370-377` correctly handles `FailoverError` instances.
- No files require special attention
<sub>Last reviewed commit: eea608d</sub>
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#22064: fix(failover): bypass models allowlist for configured fallback models
by winston-bepresent · 2026-02-20
87.3%
#10178: fix: trigger fallback when model resolution fails with unknown model
by Yida-Dev · 2026-02-06
85.3%
#9427: fix: trigger model fallback on all 4xx HTTP errors
by dbottme · 2026-02-05
84.8%
#11349: fix(agents): do not filter fallback models by models allowlist
by liuxiaopai-ai · 2026-02-07
84.2%
#19252: fix(agents): continue model fallback on failover text payloads
by mahsumaktas · 2026-02-17
82.9%
#19077: fix(agents): trigger model failover on connection-refused and netwo...
by ayanesakura · 2026-02-17
82.7%
#15815: Fallback LLM doesn't trigger if primary model is local
by shihanqu · 2026-02-13
82.2%
#13658: fix: silent model failover with fallback notification
by taw0002 · 2026-02-10
82.2%
#4462: fix: prevent gateway crash when all auth profiles are in cooldown
by garnetlyx · 2026-01-30
81.6%
#23816: fix(agents): model fallback skipped during session overrides and pr...
by ramezgaberiel · 2026-02-22
81.2%