{"record":{"id":"5b7f1fb7a96e3ac8","repo":"janhq/jan","slug":"models-endpoint-not-found-for-provider-provider","errorCode":null,"errorMessage":"Models endpoint not found for ${provider.provider}. Check the base URL configuration.","messagePattern":"Models endpoint not found for (.+?)\\. Check the base URL configuration\\.","errorType":"http","errorClass":"Error","httpStatus":404,"severity":"error","filePath":"web-app/src/services/providers/tauri.ts","lineNumber":213,"sourceCode":"          [401, 403, 429].includes(response.status) &&\n          ki < keyAttempts.length - 1\n        ) {\n          continue\n        }\n\n        if (!response.ok) {\n          if (response.status === 401) {\n            throw new Error(\n              `Authentication failed: API key is required or invalid for ${provider.provider}`\n            )\n          }\n          if (response.status === 403) {\n            throw new Error(\n              `Access forbidden: Check your API key permissions for ${provider.provider}`\n            )\n          }\n          if (response.status === 404) {\n            throw new Error(\n              `Models endpoint not found for ${provider.provider}. Check the base URL configuration.`\n            )\n          }\n          throw new Error(\n            `Failed to fetch models from ${provider.provider}: ${response.status} ${response.statusText}`\n          )\n        }\n\n        const data = await response.json()\n\n        if (data.data && Array.isArray(data.data)) {\n          return data.data\n            .map((model: { id: string }) => model.id)\n            .filter(Boolean)\n        }\n        if (Array.isArray(data)) {\n          return data\n            .filter(Boolean)","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/web-app/src/services/providers/tauri.ts#L195-L231","documentation":"Thrown by TauriProviderService.fetchModelsFromProvider (providers/tauri.ts:213) when the GET ${base_url}/models request returns 404. The provider's base URL is reachable and auth passed, but no /models route exists at that path — almost always a base_url misconfiguration where the user included or omitted a path segment.","triggerScenarios":"base_url is set to the API root (e.g. https://api.provider.com) but the code appends /models while the real path is /v1/models; or base_url already includes /v1/models and the code appends /models again yielding /v1/models/models (404); or the provider uses a non-standard models path.","commonSituations":"User copied the wrong URL from provider docs (the chat endpoint instead of the base); trailing slash or trailing /v1 inconsistency; provider renamed their API version; custom OpenAI-compatible server that does not implement /models.","solutions":["Set base_url to the versioned API root the provider documents, with no trailing slash (commonly https://api.openai.com/v1, https://api.anthropic.com, etc.). The code appends /models itself.","Ensure base_url does NOT already end with /models — that causes a double /models/models path.","Confirm the OpenAI-compatible server actually implements GET /models; some self-hosted servers do not.","Check the provider's API docs for the exact base path and version."],"exampleFix":"// before\nif (response.status === 404) {\n  throw new Error(`Models endpoint not found for ${provider.provider}. Check the base URL configuration.`)\n}\n// after: also normalize the URL before the request so /models is never doubled\nconst root = provider.base_url.replace(/\\/models\\/?$/, '').replace(/\\/$/, '')\nconst response = await fetchTauri(`${root}/models`, ...)\nif (response.status === 404) {\n  throw new Error(`Models endpoint not found at ${root}/models. Verify the base URL (no trailing /models).`)\n}","handlingStrategy":"validation","validationCode":"function normalizeProviderBaseUrl(raw: string): string {\n  // strip a user-supplied /models suffix and trailing slash; the code appends /models\n  return raw.replace(/\\/models\\/?$/, '').replace(/\\/$/, '')\n}\n// before fetchModelsFromProvider:\nif (provider.base_url) provider.base_url = normalizeProviderBaseUrl(provider.base_url)","typeGuard":"function isLikelyValidBaseUrl(u: string): boolean {\n  try { const x = new URL(u); return Boolean(x) && !u.endsWith('/models') }\n  catch { return false }\n}","tryCatchPattern":"try {\n  return await providerService.fetchModelsFromProvider(provider)\n} catch (e) {\n  if (e instanceof Error && /Models endpoint not found/.test(e.message)) {\n    toast.error(`No /models at ${provider.base_url}. Set the base URL to the versioned API root (e.g. .../v1).`)\n    return []\n  }\n  throw e\n}","preventionTips":["Normalize base_url on save to never include a trailing /models or trailing slash.","Document the expected base URL format per provider in the form's help text.","Confirm the OpenAI-compatible server implements GET /models before pointing at it.","Show the full failing URL in the error so misconfiguration is obvious."],"tags":["provider","base-url","http-status","configuration","network","tauri","typescript"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}