janhq/jan · error · Error
Unexpected error while fetching models from ${provider.provi
Error message
Unexpected error while fetching models from ${provider.provider}: ${error instanceof Error ? error.message : 'Unknown error'} What it means
The catch-all fallback in getModels for any error that is neither a structured provider error (162) nor a connection-style failure (163). Typically wraps JSON parse failures, unexpected thrown types, or runtime errors inside the data-shaping branches. It preserves the original message so it isn't lost.
Source
Thrown at web-app/src/services/providers/tauri.ts:278
if (
error instanceof Error &&
structuredErrorPrefixes.some((prefix) =>
(error as Error).message.startsWith(prefix)
)
) {
throw new Error(error.message)
}
// Provide helpful error message for any connection errors
if (error instanceof Error && error.message.includes('fetch')) {
throw new Error(
`Cannot connect to ${provider.provider} at ${provider.base_url}. Please check that the service is running and accessible.`
)
}
// Generic fallback
throw new Error(
`Unexpected error while fetching models from ${provider.provider}: ${error instanceof Error ? error.message : 'Unknown error'}`
)
}
}
async updateSettings(
providerName: string,
settings: ProviderSetting[]
): Promise<void> {
try {
// API keys are persisted to the OS keyring only (via
// register_provider_config), never to the extension's settings.json.
// Blank the key entries at this single chokepoint regardless of caller.
const isSecretKey = (key: string) =>
key === 'api-key' || key === API_KEY_FALLBACKS_SETTING_KEY
return ExtensionManager.getInstance()
.getEngine(providerName)
?.updateSettings(View on GitHub (pinned to fad3f12a14)
Solutions
- Inspect the embedded original error message - it pinpoints the real failure (e.g., 'Unexpected token < in JSON').
- Reproduce with curl to inspect the raw body and Content-Type of /models.
- If JSON parsing fails, the provider may be returning HTML with a 200 - verify the endpoint and Content-Type.
- Add a Content-Type check before calling response.json().
Example fix
// before: 200 OK with HTML body -> response.json() throws -> generic fallback
const data = await response.json()
// after: guard Content-Type before parsing
const ct = response.headers.get('content-type') ?? ''
if (!ct.includes('application/json')) {
throw new Error(`Expected JSON from ${provider.provider}/models but got '${ct}'`)
}
const data = await response.json() Defensive patterns
Strategy: try-catch
Validate before calling
async function safeJson(res: Response): Promise<unknown> {
const ct = res.headers.get('content-type') ?? ''
if (!ct.includes('application/json')) throw new Error(`Non-JSON response: ${ct}`)
return res.json()
} Type guard
function isJsonContentType(res: Response): boolean {
return (res.headers.get('content-type') ?? '').includes('application/json')
} Try / catch
try {
await provider.getModels(p)
} catch (e) {
// Anything not matching structured/connection prefixes lands here
logUnexpected(e)
showGenericError()
} Prevention
- Validate Content-Type before JSON parsing.
- Report these as bugs - they indicate an unhandled path.
- Log the raw response body on parse failure for diagnosis.
When it happens
Trigger: response.json() throws on an invalid/non-JSON body; an exception inside the .map/.filter shaping branches; a non-Error value thrown somewhere in the try block; the provider returns 200 OK with an HTML error page.
Common situations: Provider returns 200 with a non-JSON body (HTML error page, plain text); provider returns an unexpected data shape that breaks the mapping logic; a regression in the parsing branches throws a TypeError.
Related errors
- Failed to fetch models from ${provider.provider}: ${response
- Failed to fetch models from ${provider.provider}: ${lastStat
- ${error.message}
- Failed to create fallback client
- All endpoints failed
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/421c0e94ee780540.
Report an issue: GitHub.