mihomo-party-org/clash-party · error · Error

GitHub API error: ${error.message}

Error message

GitHub API error: ${error.message}

What it means

getGitHubTags calls the GitHub API (via Octokit) to list release tags for owner/repo. Any thrown Error from the request — rate limiting (403 with rate limit headers), 404 repo not found, network failure, auth failure — is caught, logged, and rethrown as 'GitHub API error: <original message>'. Non-Error throwables become the sibling 'Failed to fetch version list' error.

Source

Thrown at src/main/utils/github.ts:115

          'X-GitHub-Api-Version': GITHUB_API_CONFIG.API_VERSION
        },
        responseType: 'json',
        timeout: 10000
      }
    )

    // 更新缓存
    versionCache.set(cacheKey, {
      data: response.data,
      timestamp: Date.now()
    })

    log.debug(`Successfully fetched ${response.data.length} tags for ${owner}/${repo}`)
    return response.data
  } catch (error) {
    log.error(`Failed to fetch tags for ${owner}/${repo}`, error)
    if (error instanceof Error) {
      throw new Error(`GitHub API error: ${error.message}`)
    }
    throw new Error('Failed to fetch version list')
  }
}

/**
 * 清除版本缓存
 * @param owner 仓库所有者
 * @param repo 仓库名称
 */
export function clearVersionCache(owner: string, repo: string): void {
  const cacheKey = `${owner}/${repo}`
  const hasCache = versionCache.has(cacheKey)
  versionCache.delete(cacheKey)
  log.debug(`Cache ${hasCache ? 'cleared' : 'not found'} for ${owner}/${repo}`)
}

/**

View on GitHub (pinned to 911e090537)

Solutions

  1. Read the wrapped message: 'rate limit exceeded' means supply an authenticated token; 'Not Found' means wrong owner/repo or missing auth for a private repo.
  2. Set/provide a GitHub personal access token (even read-only) to raise the rate limit from 60 to 5000 requests/hour.
  3. Wait for the rate-limit window to reset (check x-ratelimit-reset) or back off and retry later.
  4. Check network/proxy connectivity to api.github.com (curl https://api.github.com/repos/MetaCubeX/mihomo/tags).
  5. Use a cached/last-known version list while the API is unreachable.

Example fix

// before
const tags = await getGitHubTags('MetaCubeX', 'mihomo')
// after: token + retry with backoff
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
let tags
for (let attempt = 0; attempt < 3; attempt++) {
  try {
    tags = await octokit.repos.listTags({ owner: 'MetaCubeX', repo: 'mihomo' }).data
    break
  } catch (e) {
    if (attempt === 2) throw e
    await new Promise((r) => setTimeout(r, 2 ** attempt * 1000))
  }
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight: cheap connectivity + rate-limit check before calling the API
const res = await fetch('https://api.github.com/rate_limit')
const { remaining } = (await res.json()).resources.core
if (remaining === 0) throw new Error('GitHub rate limit exhausted; configure a token or wait for reset')

Try / catch

try {
  const tags = await getGitHubTags(owner, repo)
} catch (e) {
  const msg = e instanceof Error ? e.message : String(e)
  if (msg.includes('rate limit')) {
    await waitUntilRateLimitReset()
    return getGitHubTags(owner, repo)
  }
  if (msg.includes('Not Found')) {
    // repo renamed/private: check auth or repo name
  }
  return cachedTags // stale-but-usable fallback
}

Prevention

When it happens

Trigger: fetchMihomoTags -> getGitHubTags with: unauthenticated requests exceeding 60/hour rate limit, network offline/proxy failure, repo renamed or private without a GITHUB_TOKEN, or GitHub 5xx outage.

Common situations: CI or long-running Electron app polling updates without a token and hitting the anonymous rate limit; corporate proxy blocking api.github.com; user in a region where GitHub is intermittently unreachable; checking versions during a GitHub incident.

Related errors


AI-assisted analysis of mihomo-party-org/clash-party@911e090537 (2026-08-30). Data as JSON: /api/errors/6c54d6a9862e9260. Report an issue: GitHub.