{"record":{"id":"f532341fc573d365","repo":"janhq/jan","slug":"failed-to-fetch-model-catalog-response-status","errorCode":null,"errorMessage":"Failed to fetch model catalog: ${response.status} ${response.statusText}","messagePattern":"Failed to fetch model catalog: (.+?) (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"web-app/src/services/models/default.ts","lineNumber":65,"sourceCode":"  async getModelContextLimit(modelId: string, provider: string): Promise<number | undefined> {\n    const engine = this.getEngine(provider) as (AIEngine & {\n      getModelContextLimit?: (id: string) => Promise<number | undefined>\n    }) | undefined\n    if (!engine?.getModelContextLimit)\n      throw new Error('This provider cannot read model context limits. Update its extension or check provider settings.')\n    return engine.getModelContextLimit(modelId)\n  }\n\n  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","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/janhq/jan/blob/7205d770c1e097c3daf35a911176410e93bc5564/web-app/src/services/models/default.ts#L47-L83","documentation":"fetchModelCatalog downloads the remote model catalog JSON from MODEL_CATALOG_URL; any non-OK HTTP response throws with the status and status text. The inner message is then rewrapped by the catch block with the same prefix.","triggerScenarios":"MODEL_CATALOG_URL returns 404/403/500, is rate-limited (429), or an intercepting proxy returns an error page.","commonSituations":"Catalog URL changed or moved after an app update, CDN outage, offline/behind-firewall environment, or GitHub raw content rate limiting.","solutions":["Check network connectivity and retry","Verify MODEL_CATALOG_URL is correct and the catalog is published","Use a cached/bundled fallback catalog when fetch fails","Check for corporate proxy interference"],"exampleFix":"// before\nif (!response.ok) {\n  throw new Error(`Failed to fetch model catalog: ${response.status} ${response.statusText}`)\n}\n// after\nif (!response.ok) {\n  if (response.status === 429) throw new Error('Model catalog rate limited, retry later')\n  return fallbackCatalog // use bundled copy\n}","handlingStrategy":"fallback","validationCode":"const res = await fetch(MODEL_CATALOG_URL)\nif (!res.ok) return cachedCatalog","typeGuard":"null","tryCatchPattern":"try { catalog = await fetchModelCatalog() } catch { catalog = bundledFallbackCatalog }","preventionTips":["Bundle a snapshot catalog for offline/error fallback","Monitor the catalog URL with uptime checks","Cache the last successful catalog locally"],"tags":["network","http","catalog"],"backgroundTag":"http-error-response","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"}