MHSanaei/3x-ui · error
GitHub API error: %s
Error message
GitHub API error: %s
What it means
Returned by the release-list fetch (GetXrayVersions path) when the GitHub API answers non-200 AND the JSON body contains a non-empty "message" field — GitHub's standard error envelope. The message is surfaced verbatim, e.g. 'API rate limit exceeded for 1.2.3.4' or 'Bad credentials'. It exists because the caller expects an array of releases but GitHub returns an error object, which would otherwise fail confusingly during json.Unmarshal.
Source
Thrown at internal/web/service/server.go:798
req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, XrayURL, nil)
if reqErr != nil {
return nil, reqErr
}
resp, err := s.settingService.NewProxiedHTTPClient(10 * time.Second).Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// Check HTTP status code - GitHub API returns object instead of array on error
if resp.StatusCode != http.StatusOK {
bodyBytes, _ := io.ReadAll(resp.Body)
var errorResponse struct {
Message string `json:"message"`
}
if json.Unmarshal(bodyBytes, &errorResponse) == nil && errorResponse.Message != "" {
return nil, fmt.Errorf("GitHub API error: %s", errorResponse.Message)
}
return nil, fmt.Errorf("GitHub API returned status %d: %s", resp.StatusCode, resp.Status)
}
buffer := bytes.NewBuffer(make([]byte, bufferSize))
buffer.Reset()
if _, err := buffer.ReadFrom(resp.Body); err != nil {
return nil, err
}
var releases []Release
if err := json.Unmarshal(buffer.Bytes(), &releases); err != nil {
return nil, err
}
var versions []string
for _, release := range releases {
tagVersion := strings.TrimPrefix(release.TagName, "v")View on GitHub (pinned to ad32144c42)
Solutions
- Read the message: 'rate limit' → wait for the hourly window reset or supply GITHUB_TOKEN; 'Bad credentials' → fix or remove the token
- Reduce the polling frequency of version checks
- Retry after the Retry-After / X-RateLimit-Reset window indicated by the API
Defensive patterns
Strategy: retry
Try / catch
versions, err := svc.GetXrayVersions()
if err != nil && strings.Contains(err.Error(), "rate limit") {
// wait for X-RateLimit-Reset window or set GITHUB_TOKEN, then retry
}
Prevention
- Use an authenticated token for GitHub API calls on shared IPs
- Cache the release list with a TTL of minutes, not seconds
- Distinguish message-bearing errors (this one) from bare status errors when choosing retry policy
When it happens
Trigger: Unauthenticated quota exhausted (403 + rate-limit message); an invalid GITHUB_TOKEN (401 'Bad credentials'); requesting the XTLS/Xray-core releases list too often from a shared IP.
Common situations: Panels on cloud NAT IPs sharing the 60 req/h anonymous budget; update checks on a cron with short intervals; a revoked or typo'd token in the environment.
Related errors
- GitHub API returned status %d: %s
- GitHub API returned status %d: %s
- download xray: unexpected HTTP %d
- download xray checksum: unexpected HTTP %d
- xray version %q is not in the fetched release list
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/d85ba58adc6ef1ea.
Report an issue: GitHub.