{"record":{"id":"83916f91595747cd","repo":"janhq/jan","slug":"provider-must-have-base-url-configured","errorCode":null,"errorMessage":"Provider must have base_url configured","messagePattern":"Provider must have base_url configured","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web-app/src/lib/remoteModelCatalog.ts","lineNumber":205,"sourceCode":"  }\n  return {\n    ok: response.ok,\n    status: response.status,\n    statusText: response.statusText,\n    body,\n  }\n}\n\nexport async function fetchTopRemoteModels(\n  provider: ProviderLike,\n  fetchImpl: FetchImpl\n): Promise<RemoteCatalogModel[]> {\n  const kind = resolveCatalogKind(provider)\n  if (!kind) {\n    throw new Error(`Catalog not supported for ${provider.provider}`)\n  }\n  if (!provider.base_url) {\n    throw new Error('Provider must have base_url configured')\n  }\n\n  const keyChain = providerRemoteApiKeyChain(provider)\n  const attempts: (string | undefined)[] = keyChain.length > 0 ? keyChain : [undefined]\n\n  let lastStatus = 0\n  let lastStatusText = ''\n  for (let i = 0; i < attempts.length; i++) {\n    const result = await getJson(\n      fetchImpl,\n      `${provider.base_url}/models`,\n      buildHeaders(provider, attempts[i])\n    )\n    lastStatus = result.status\n    lastStatusText = result.statusText\n    if (!result.ok) {\n      if ([401, 403, 429].includes(result.status) && i < attempts.length - 1) continue\n      throw new Error(`Failed to fetch models from ${provider.provider}: ${result.status} ${result.statusText}`)","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/web-app/src/lib/remoteModelCatalog.ts#L187-L223","documentation":"Thrown by fetchTopRemoteModels (remoteModelCatalog.ts:205) when a remote catalog provider object has no base_url. The function needs base_url to construct the `${base_url}/models` GET request and refuses to proceed without it. Distinct from the identical-message error in providers/tauri.ts:149 which is a different code path.","triggerScenarios":"A provider passed to fetchTopRemoteModels has base_url === undefined, null, or '' (empty string). The catalog kind check already passed, so this is a partially-configured provider that has a known flavor but no endpoint.","commonSituations":"Custom provider created with only an API key and no endpoint; base_url field cleared by the user but provider not deleted; provider object deserialized from old config that predates the base_url field.","solutions":["In Settings > Model Providers, set the provider's base URL (e.g. https://api.openai.com/v1).","Validate provider.base_url in the provider-edit form before saving so the catalog call cannot be reached without it.","If the provider is local (no public endpoint), do not offer the 'browse remote models' action for it."],"exampleFix":"// before\nif (!provider.base_url) {\n  throw new Error('Provider must have base_url configured')\n}\n// after: guard at the caller, never reach fetchTopRemoteModels\nif (!provider.base_url?.trim()) {\n  return { models: [], error: 'missing_base_url' }\n}","handlingStrategy":"validation","validationCode":"function hasBaseUrl(p: ProviderLike): boolean {\n  return typeof p.base_url === 'string' && p.base_url.trim().length > 0\n}\n// before fetchTopRemoteModels:\nif (!hasBaseUrl(provider)) {\n  return { models: [], error: 'missing_base_url' }\n}","typeGuard":"function providerHasBaseUrl(p: unknown): p is ProviderLike & { base_url: string } {\n  return typeof p === 'object' && p !== null && typeof (p as ProviderLike).base_url === 'string' && (p as ProviderLike).base_url!.trim().length > 0\n}","tryCatchPattern":"try {\n  return await fetchTopRemoteModels(provider, fetchImpl)\n} catch (e) {\n  if (e instanceof Error && e.message === 'Provider must have base_url configured') {\n    promptForBaseUrl(provider.provider)\n    return []\n  }\n  throw e\n}","preventionTips":["Require base_url in the provider-edit form before save.","Pre-fill known providers' base URLs from a defaults table.","Disable the catalog action when base_url is empty.","Normalize base_url on save (trim, strip trailing slash)."],"tags":["catalog","provider","configuration","base-url","validation","typescript"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}