janhq/jan · error · Error
Failed to fetch models from ${provider.provider}: ${result.s
Error message
Failed to fetch models from ${provider.provider}: ${result.status} ${result.statusText} What it means
Thrown inside the fetchTopRemoteModels key-rotation loop when a GET to `${provider.base_url}/models` returns non-OK and either the status is not in [401,403,429] or this is the final key attempt (remoteModelCatalog.ts:223). This is the in-loop terminal error carrying the exact failing status/statusText.
Source
Thrown at web-app/src/lib/remoteModelCatalog.ts:223
throw new Error('Provider must have base_url configured')
}
const keyChain = providerRemoteApiKeyChain(provider)
const attempts: (string | undefined)[] = keyChain.length > 0 ? keyChain : [undefined]
let lastStatus = 0
let lastStatusText = ''
for (let i = 0; i < attempts.length; i++) {
const result = await getJson(
fetchImpl,
`${provider.base_url}/models`,
buildHeaders(provider, attempts[i])
)
lastStatus = result.status
lastStatusText = result.statusText
if (!result.ok) {
if ([401, 403, 429].includes(result.status) && i < attempts.length - 1) continue
throw new Error(`Failed to fetch models from ${provider.provider}: ${result.status} ${result.statusText}`)
}
const body = result.body as { data?: unknown }
const rows = Array.isArray(body?.data) ? (body.data as unknown[]) : []
return normalizeCatalog(kind, rows)
}
throw new Error(`Failed to fetch models from ${provider.provider}: ${lastStatus} ${lastStatusText}`)
}
function normalizeCatalog(kind: CatalogKind, rows: unknown[]): RemoteCatalogModel[] {
const inferCaps =
kind === 'openai'
? inferOpenAICapabilities
: kind === 'gemini'
? inferGeminiCapabilities
: inferAnthropicCapabilities
View on GitHub (pinned to fad3f12a14)
Solutions
- Read the status code in the message: 404 → fix base_url (usually should end at /v1, not include /models); 5xx → retry later or check provider status page; 401/403 on last key → refresh API key.
- Confirm base_url does not already end with /models — the code appends /models itself.
- For 429, reduce catalog refresh frequency or add backoff.
- Verify the provider's actual /models path against their API docs (some use /v1/models, some /openai/v1/models).
Example fix
// before
throw new Error(`Failed to fetch models from ${provider.provider}: ${result.status} ${result.statusText}`)
// after: include the response body hint when available
const hint = result.body?.error?.message ?? result.statusText
throw new Error(`Failed to fetch models from ${provider.provider}: ${result.status} ${hint}`) Defensive patterns
Strategy: try-catch
Validate before calling
function normalizeCatalogBaseUrl(raw: string): string {
// the code appends /models itself; strip a user-supplied /models suffix
return raw.replace(/\/models\/?$/, '').replace(/\/$/, '')
}
// before the loop:
provider.base_url = normalizeCatalogBaseUrl(provider.base_url!) Type guard
function isCatalogOk(r: { ok: boolean; status: number }): boolean {
return r.ok || ![401, 403, 404, 429, 500, 502, 503].includes(r.status)
} Try / catch
try {
return await fetchTopRemoteModels(provider, fetchImpl)
} catch (e) {
if (e instanceof Error && /Failed to fetch models/.test(e.message)) {
toast.error(e.message)
return [] // degrade catalog UI instead of crashing
}
throw e
} Prevention
- Normalize base_url so it never ends with /models (the code appends it).
- Cache the last successful catalog result for graceful degradation.
- Rate-limit catalog refreshes to avoid 429s.
- Show the status code in the UI so users can distinguish 404 (URL) from 401 (key).
When it happens
Trigger: The /models endpoint returns 404 (wrong base URL path), 500/502/503 (provider outage), 400 (malformed request), or — on the last key of the chain — a 401/403/429 that exhausts rotation.
Common situations: User set base_url with a trailing path that already includes /models so the request hits /models/models (404); provider deprecated the v1 endpoint; transient 502 from a load balancer; the provider requires a different auth scheme so every key 401s.
Related errors
- Models endpoint not found for ${provider.provider}. Check th
- Provider must have base_url configured
- Failed to fetch model catalog: ${response.status} ${response
- Authentication failed: API key is required or invalid for ${
- Catalog not supported for ${provider.provider}
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/426298b62b10ed9f.
Report an issue: GitHub.