janhq/jan · error · Error
Failed to fetch models from ${provider.provider}: ${response
Error message
Failed to fetch models from ${provider.provider}: ${response.status} ${response.statusText} What it means
Thrown by getModels when the provider's GET {base_url}/models returns a non-OK HTTP status that is not one of the specifically handled codes (401/403/404). It surfaces the raw status code and status text so the caller can diagnose provider-side problems (5xx outages, 429 rate limits, 400 bad requests, 405 method not allowed, etc.). It is the catch-all for any !response.ok branch the method does not recognize.
Source
Thrown at web-app/src/services/providers/tauri.ts:217
}
if (!response.ok) {
if (response.status === 401) {
throw new Error(
`Authentication failed: API key is required or invalid for ${provider.provider}`
)
}
if (response.status === 403) {
throw new Error(
`Access forbidden: Check your API key permissions for ${provider.provider}`
)
}
if (response.status === 404) {
throw new Error(
`Models endpoint not found for ${provider.provider}. Check the base URL configuration.`
)
}
throw new Error(
`Failed to fetch models from ${provider.provider}: ${response.status} ${response.statusText}`
)
}
const data = await response.json()
if (data.data && Array.isArray(data.data)) {
return data.data
.map((model: { id: string }) => model.id)
.filter(Boolean)
}
if (Array.isArray(data)) {
return data
.filter(Boolean)
.map((model) =>
typeof model === 'object' && 'id' in model ? model.id : model
)
}View on GitHub (pinned to fad3f12a14)
Solutions
- Read the numeric status embedded in the message: 5xx -> provider is down, retry later / check the provider status page; 429 -> rate limit, reduce frequency or use a key with higher quota; 4xx other than auth -> verify base_url path and that the provider implements /models.
- Reproduce with curl: curl -i -H "x-api-key: $KEY" -H "Authorization: Bearer $KEY" ${base_url}/models to see the raw response.
- Verify provider configuration (base_url, custom_header entries) in provider settings.
- If 429 persists across all keys, back off, rotate keys, or switch providers.
Example fix
// before: every non-ok non-401/403/404 falls into one generic throw
if (!response.ok) {
if (response.status === 404) { /* ... */ }
throw new Error(`Failed to fetch models from ${provider.provider}: ${response.status} ${response.statusText}`)
}
// after: branch transient 5xx / 429 for caller retry guidance
if (!response.ok) {
if (response.status >= 500 || response.status === 429) {
throw new Error(`${provider.provider} is temporarily unavailable (${response.status}). Retry shortly.`)
}
throw new Error(`Failed to fetch models from ${provider.provider}: ${response.status} ${response.statusText}`)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight the endpoint before listing models
async function isModelsEndpointOk(base_url: string, key?: string): Promise<boolean> {
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
if (key) { headers['x-api-key'] = key; headers['Authorization'] = `Bearer ${key}` }
try {
const res = await fetch(`${base_url}/models`, { method: 'GET', headers })
return res.ok
} catch { return false }
} Type guard
function isNonOkStatus(status: number): boolean {
return !(status >= 200 && status < 300)
} Try / catch
try {
const models = await provider.getModels(providerConfig)
} catch (e) {
const msg = e instanceof Error ? e.message : ''
const m = msg.match(/:\s(\d{3})\s/)
if (m) {
const status = Number(m[1])
if (status >= 500 || status === 429) scheduleRetry()
else showUserError(msg)
} else throw e
} Prevention
- Validate base_url reachability before saving provider settings.
- Handle 5xx/429 with retry and exponential backoff in the UI layer.
- Cache the last successful model list so transient provider errors don't blank the UI.
When it happens
Trigger: The fetchTauri call to ${provider.base_url}/models resolved with response.ok === false AND response.status is not 401, 403, or 404. Concretely: 500/502/503 server errors, 429 on the final key attempt (the continue branch is skipped when ki === keyAttempts.length-1), 400 Bad Request, 405 Method Not Allowed.
Common situations: Provider service is up but erroring (5xx); rate-limited after exhausting the whole API-key chain (429); base_url points at a valid host but wrong path returning 400; provider does not implement an OpenAI-compatible GET /models; temporary provider outage.
Related errors
- API request failed with status ${response.status}: ${JSON.st
- HTTP request failed: {0}
- Failed to fetch models from ${provider.provider}: ${lastStat
- ${error.message}
- Cannot connect to ${provider.provider} at ${provider.base_ur
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/885d69eb19c1dbfe.
Report an issue: GitHub.