#23800: fix: map HTTP 404 to model_not_found failover reason
agents
size: XS
Cluster:
Model Fallback and Error Handling
#### Summary
HTTP 404 errors from model providers (e.g. Google Gemini's "Requested entity was not found") break the failover chain. `resolveFailoverReasonFromError()` handles 402/429/401/403/408/503/400 but **not 404**, so bare 404s propagate as unrecoverable errors instead of triggering model fallback.
Closes #4992
Supersedes #11446 (irreconcilable branch conflicts after upstream added `model_not_found`)
References #4997, #6735, #9427, #10262, #14510 (prior attempts, all closed unmerged)
lobster-biscuit
#### Root Cause
Since upstream added `model_not_found` as a `FailoverReason` (with `resolveFailoverStatus` mapping and `isModelNotFoundErrorMessage` classification), most model-not-found scenarios are covered. However, `resolveFailoverReasonFromError()` still lacks a `status === 404` case. When a provider returns HTTP 404 with a message that doesn't contain "model" (e.g. Google's "Requested entity was not found"), message-based classification misses it and the failover chain breaks.
#### Fix
Single line: `if (status === 404) return "model_not_found"` in `resolveFailoverReasonFromError()`. Uses the existing `model_not_found` reason — no new types needed.
#### False-Positive Risk
Low. `resolveFailoverReasonFromError()` is only called from `coerceToFailoverError()` in the model fallback loop catch block. Only LLM provider HTTP errors flow through this path — file/config/module errors use different error codes (ENOENT, ERR_MODULE_NOT_FOUND).
#### Tests
- `failover-error.test.ts`: 3 new cases (404 → model_not_found, provider entity error, coercion with metadata)
- `model-fallback.test.ts`: 1 integration case (bare 404 from primary → cascade to fallback)
```
pnpm vitest run src/agents/failover-error.test.ts src/agents/model-fallback.test.ts
# Test Files 2 passed (2)
# Tests 37 passed (37)
```
TypeScript: `pnpm tsgo` — no new errors
Lint: `pnpm check` — clean
#### Files Changed
| File | Change |
|------|--------|
| `src/agents/failover-error.ts` | Add `status === 404 → "model_not_found"` in `resolveFailoverReasonFromError()` |
| `src/agents/failover-error.test.ts` | 3 new test cases |
| `src/agents/model-fallback.test.ts` | 1 integration test case |
#### Sign-Off
- Models used: Claude Opus 4.6 (implementation) + ChatGPT 5.3 Codex (verification)
- Testing: Fully tested — 37/37 pass, no regressions
- Verification: ChatGPT 5.3 Codex independently traced the full failover pipeline through `model-fallback.ts` → `coerceToFailoverError()` → `resolveFailoverReasonFromError()`, confirmed the gap, validated the fix approach, and verified false-positive risk is low since only LLM provider HTTP errors flow through this path
- The fix is intentionally minimal: 1 line of logic + 4 test cases. Previous attempts (#4997, #6735, #9427, #10262) each missed at least one of the four gaps in the pipeline; upstream has since closed three of those gaps, leaving only this status-code mapping.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Verified-By: ChatGPT 5.3 Codex <noreply@openai.com>
Most Similar PRs
#9427: fix: trigger model fallback on all 4xx HTTP errors
by dbottme · 2026-02-05
80.2%
#21152: fix(agents): throw FailoverError for unknown model so fallback chai...
by Mellowambience · 2026-02-19
78.9%
#21017: fix: treat HTTP 502/503/504 as failover-eligible (timeout reason)
by taw0002 · 2026-02-19
78.6%
#22064: fix(failover): bypass models allowlist for configured fallback models
by winston-bepresent · 2026-02-20
78.0%
#11821: fix(auth): trigger failover on 401 status code from expired OAuth t...
by AnonO6 · 2026-02-08
76.7%
#21049: fix(failover): treat HTTP 5xx as rate-limit for model fallback
by maximalmargin · 2026-02-19
75.5%
#19077: fix(agents): trigger model failover on connection-refused and netwo...
by ayanesakura · 2026-02-17
75.4%
#19252: fix(agents): continue model fallback on failover text payloads
by mahsumaktas · 2026-02-17
75.2%
#15815: Fallback LLM doesn't trigger if primary model is local
by shihanqu · 2026-02-13
73.7%
#10178: fix: trigger fallback when model resolution fails with unknown model
by Yida-Dev · 2026-02-06
73.6%