{"record":{"id":"f279ce9beb027bae","repo":"janhq/jan","slug":"failed-to-fetch-model-catalog-error-instanceof","errorCode":null,"errorMessage":"Failed to fetch model catalog: ${error instanceof Error ? error.message : 'Unknown error'}","messagePattern":"Failed to fetch model catalog: (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web-app/src/services/models/default.ts","lineNumber":63,"sourceCode":"  async fetchModels(): Promise<modelInfo[]> {\n    return this.getEngine()?.list() ?? []\n  }\n\n  async fetchModelCatalog(): Promise<ModelCatalog> {\n    try {\n      const response = await fetch(MODEL_CATALOG_URL)\n\n      if (!response.ok) {\n        throw new Error(\n          `Failed to fetch model catalog: ${response.status} ${response.statusText}`\n        )\n      }\n\n      const catalog: ModelCatalog = await response.json()\n      return catalog\n    } catch (error) {\n      console.error('Error fetching model catalog:', error)\n      throw new Error(\n        `Failed to fetch model catalog: ${error instanceof Error ? error.message : 'Unknown error'}`\n      )\n    }\n  }\n\n  async fetchLatestJanModel(): Promise<CatalogModel | null> {\n    try {\n      const response = await fetch(LATEST_JAN_MODEL_URL)\n\n      if (!response.ok) {\n        console.error(\n          `Failed to fetch latest Jan model: ${response.status} ${response.statusText}`\n        )\n        return null\n      }\n\n      const data = await response.json()\n","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/web-app/src/services/models/default.ts#L45-L81","documentation":"Thrown by the catch block of ModelService.fetchModelCatalog (default.ts:63). It wraps any error from the try block — the inner 'response not ok' throw [153], a network failure (DNS, offline, TLS), or a JSON parse failure — into a uniform 'Failed to fetch model catalog' message with the underlying error.message or 'Unknown error'.","triggerScenarios":"fetch() itself rejects (network unreachable, DNS failure, CORS, TLS error), response.json() throws (non-JSON body / empty body), or the inner [153] throw propagates into the catch and gets re-wrapped (note: the inner status detail is lost, replaced by the Error.message).","commonSituations":"User is offline; DNS for the catalog host fails; a proxy returns an HTML error page that fails JSON parsing; TLS cert expired on the catalog endpoint; the inner [153] error gets double-wrapped, obscuring the original status.","solutions":["Distinguish network errors from HTTP-status errors so the inner [153] status is not lost — rethrow known Error instances unchanged.","Verify network connectivity and DNS resolution for the catalog host.","If a proxy intercepts with HTML, whitelist the catalog host.","Cache the last successful catalog locally so a fetch failure degrades gracefully."],"exampleFix":"// before\ncatch (error) {\n  console.error('Error fetching model catalog:', error)\n  throw new Error(`Failed to fetch model catalog: ${error instanceof Error ? error.message : 'Unknown error'}`)\n}\n// after: preserve the structured inner error instead of re-wrapping it\ncatch (error) {\n  console.error('Error fetching model catalog:', error)\n  if (error instanceof Error && error.message.startsWith('Failed to fetch model catalog')) throw error\n  throw new Error(`Failed to fetch model catalog: ${error instanceof Error ? error.message : 'Unknown error'}`)\n}","handlingStrategy":"try-catch","validationCode":"async function safeFetchJson(url: string): Promise<ModelCatalog | null> {\n  const r = await fetch(url)\n  if (!r.ok) return null\n  try { return await r.json() as ModelCatalog } catch { return null }\n}","typeGuard":"function isModelCatalog(x: unknown): x is ModelCatalog {\n  return typeof x === 'object' && x !== null && Array.isArray((x as ModelCatalog).models)\n}","tryCatchPattern":"try {\n  return await this.fetchModelCatalog()\n} catch (error) {\n  console.error('catalog unavailable, degrading to cache', error)\n  return getCachedCatalog() ?? []\n}","preventionTips":["Do not re-wrap an Error that already carries the catalog-failure message — rethrow it.","Distinguish fetch() network rejection from response.json() parse failure.","Cache successful catalog fetches so a failure degrades gracefully.","Show a non-blocking banner rather than failing the whole Hub view."],"tags":["catalog","network","error-handling","fetch","typescript"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}