{"record":{"id":"b4c609f534a908a0","repo":"mihomo-party-org/clash-party","slug":"failed-to-fetch-version-list","errorCode":null,"errorMessage":"Failed to fetch version list","messagePattern":"Failed to fetch version list","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/utils/github.ts","lineNumber":117,"sourceCode":"        responseType: 'json',\n        timeout: 10000\n      }\n    )\n\n    // 更新缓存\n    versionCache.set(cacheKey, {\n      data: response.data,\n      timestamp: Date.now()\n    })\n\n    log.debug(`Successfully fetched ${response.data.length} tags for ${owner}/${repo}`)\n    return response.data\n  } catch (error) {\n    log.error(`Failed to fetch tags for ${owner}/${repo}`, error)\n    if (error instanceof Error) {\n      throw new Error(`GitHub API error: ${error.message}`)\n    }\n    throw new Error('Failed to fetch version list')\n  }\n}\n\n/**\n * 清除版本缓存\n * @param owner 仓库所有者\n * @param repo 仓库名称\n */\nexport function clearVersionCache(owner: string, repo: string): void {\n  const cacheKey = `${owner}/${repo}`\n  const hasCache = versionCache.has(cacheKey)\n  versionCache.delete(cacheKey)\n  log.debug(`Cache ${hasCache ? 'cleared' : 'not found'} for ${owner}/${repo}`)\n}\n\n/**\n * 下载 GitHub Release 资产\n * @param url 下载 URL","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/mihomo-party-org/clash-party/blob/911e090537acdf7c50bee1c3aebecc2ef119a8b5/src/main/utils/github.ts#L99-L135","documentation":"The fallback branch of getGitHubTags: when the caught value is NOT an Error instance (e.g. a string was thrown, or an odd rejection shape from an underlying fetch layer), it throws the generic message 'Failed to fetch version list'. It carries less information than error 82 by design, since the original throwable's message could not be safely extracted.","triggerScenarios":"fetchMihomoTags -> getGitHubTags where the underlying request layer rejects with a non-Error value — e.g. a thrown string, an undefined rejection, or a proxy library rejecting with a plain object.","commonSituations":"Custom request adapters/interceptors that reject with strings; patched or mocked HTTP layers in odd environments; older versions of the transport rejecting without Error objects.","solutions":["Check the app log line 'Failed to fetch tags for owner/repo' which contains the original raw value for the real cause.","Test connectivity to api.github.com directly; treat as a network/API availability problem first.","If a custom transport/interceptor is in play, make it reject with Error instances instead of strings.","Add a retry with backoff, since the cause is usually transient network failure."],"exampleFix":"// before: transport rejecting with a string\nreject('timeout')\n// after\nreject(new Error(`timeout`))","handlingStrategy":"fallback","validationCode":"// preflight connectivity check\nconst online = await fetch('https://api.github.com', { method: 'HEAD' }).then((r) => r.ok).catch(() => false)\nif (!online) throw new Error('api.github.com unreachable')","typeGuard":null,"tryCatchPattern":"let tags: Tag[]\ntry {\n  tags = await getGitHubTags(owner, repo)\n} catch (e) {\n  log.warn('Version check failed, using cached list', e)\n  tags = cachedTags ?? []\n}","preventionTips":["Reject with Error instances (never strings) in any custom transport/interceptor","Cache the last successful tag list for offline fallback","Check basic GitHub connectivity before version checks","Keep the app functional with the last-known version when the API is down"],"tags":["network","github-api","error-handling"],"backgroundTag":"github-api-rate-limit","analyzedSha":"911e090537acdf7c50bee1c3aebecc2ef119a8b5","analyzedAt":"2026-08-30T13:00:49.174Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}