siyuan-note/siyuan · error
get GitHub releases failed: %d
Error message
get GitHub releases failed: %d
What it means
Returned by fetchGitHubReleases(ctx) when the GitHub API responds with a status code other than 200. The numeric status is embedded in the message. The most common cause is 403 (rate limit) since the GitHub releases endpoint is unauthenticated. The error is logged via logging.LogError before returning.
Source
Thrown at kernel/model/updater_release.go:247
return nil, err
}
return value.([]*githubRelease), nil
}
func fetchGitHubReleases(ctx context.Context) ([]*githubRelease, error) {
releases := []*githubRelease{}
request := httpclient.NewCloudRequest30s().
SetContext(ctx).
SetHeader("Accept", "application/vnd.github+json").
SetHeader("X-GitHub-Api-Version", "2022-11-28").
SetSuccessResult(&releases)
response, err := request.Get(githubReleasesURL)
if err != nil {
logging.LogErrorf("get GitHub releases failed: %s", err)
return nil, err
}
if 200 != response.StatusCode {
err = fmt.Errorf("get GitHub releases failed: %d", response.StatusCode)
logging.LogError(err.Error())
return nil, err
}
if 0 == len(releases) {
return nil, errors.New("GitHub releases are empty")
}
githubReleasesLock.Lock()
cachedGitHubReleases = releases
githubReleasesCacheTime = time.Now().Unix()
githubReleasesLock.Unlock()
return releases, nil
}
func selectGitHubRelease(releases []*githubRelease, channel string) *githubRelease {
var selected *githubRelease
for _, release := range releases {
if nil == release || release.Draft || !isReleaseAllowed(channel, release.TagName) {View on GitHub (pinned to 251596fc0d)
Solutions
- Wait for the rate-limit window to reset (check X-RateLimit-Reset header) and retry; the cache (6h, githubAPIReleaseCacheSeconds) reduces repeated calls.
- Avoid force=true refreshes in loops — they bypass the cache and burn quota.
- Switch to the stable channel, which uses the rhy cloud endpoint and does not hit the GitHub API.
- If behind a shared NAT, consider routing GitHub API traffic through a distinct egress IP.
Defensive patterns
Strategy: retry
Try / catch
// Retry with backoff; respect GitHub rate limits.
var releases []*githubRelease
for attempt := 0; attempt < 3; attempt++ {
rel, err := getGitHubReleases(ctx, attempt == 0)
if err == nil {
releases = rel
break
}
if !strings.Contains(err.Error(), "get GitHub releases failed") {
break // different error; don't retry blindly
}
time.Sleep(time.Duration(1<<attempt) * time.Minute)
} Prevention
- Never call getGitHubReleases with force=true in a loop — it bypasses the 6h cache.
- Prefer the stable channel (rhy endpoint) when many clients share one IP.
- Surface 'rate limited, try later' to the user instead of a raw error.
When it happens
Trigger: GitHub API returns 403 due to unauthenticated rate limiting (60 requests/hour per IP, shared across users behind a NAT/corporate proxy). 5xx during a GitHub outage. 304/other non-200 responses. A proxy returning an unexpected status.
Common situations: Many machines behind one public IP (office, CI, NAT) exhausting the 60/hour anonymous quota. GitHub incident. Corporate proxy intercepting api.github.com. Repeated force=true refreshes burning the quota.
Related errors
- GitHub releases are empty
- get checksum manifest failed: %d
- send article to liandi failed [sc=%d]
- stable release version is invalid
- checksum manifest response is empty
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/caa9cc1aecf80f3a.
Report an issue: GitHub.