{"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":null,"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/7205d770c1e097c3daf35a911176410e93bc5564/web-app/src/lib/remoteModelCatalog.ts#L187-L223","documentation":"fetchTopRemoteModels throws this when the provider passes the catalog-kind check but has no base_url, which is mandatory to issue the models-list HTTP request. It is an early configuration validation so the caller gets a clear message instead of a malformed-URL fetch failure.","triggerScenarios":"Remote provider record saved without a base URL; base_url deleted or whitespace-only during editing; provider created programmatically with only provider name and api_key.","commonSituations":"Fresh provider entry where the user added a key but not the endpoint; migration/import dropping base_url; templated providers expecting the user to fill in the URL.","solutions":["Set the provider's base_url in Settings > Model Providers (e.g. https://api.openai.com/v1).","Validate base_url is a non-empty, well-formed URL before calling fetchTopRemoteModels.","For catalog UI, hide/disable 'fetch models' until base_url is populated."],"exampleFix":"// before\nprovider.base_url = undefined\n// after\nprovider.base_url = 'https://api.openai.com/v1'","handlingStrategy":"validation","validationCode":"let u\ntry { u = new URL(provider.base_url || '') } catch { u = null }\nif (!u || !/^https?:$/.test(u.protocol)) { promptForBaseUrl(provider); return }","typeGuard":"const hasBaseUrl = (p) => typeof p.base_url === 'string' && p.base_url.trim().length > 0","tryCatchPattern":"try { await fetchTopRemoteModels(provider, fetch) } catch (e) { if (e.message.includes('base_url configured')) { openBaseUrlSettings(provider) } else throw e }","preventionTips":["Require base_url in the provider edit form (non-empty, valid URL).","Validate provider records before persisting them.","Disable remote-catalog actions until the endpoint is configured."],"tags":["configuration","base-url","provider","validation"],"backgroundTag":"missing-required-config-field","analyzedSha":"7205d770c1e097c3daf35a911176410e93bc5564","analyzedAt":"2026-09-17T14:27:30.100Z","contentChangedAt":"2026-09-17T14:27:30.100Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}