MHSanaei/3x-ui · error

GitHub API returned status %d: %s

Error message

GitHub API returned status %d: %s

What it means

Returned by the same release-list fetch when the response is non-200 but the body has no parseable "message" JSON field, so only the status code and resp.Status can be reported. Covers non-GitHub-shaped failures: proxy error pages, HTML block pages, 5xx bodies. Distinguishes transport-level rejection from GitHub's structured errors (which produce the 'GitHub API error' variant).

Source

Thrown at internal/web/service/server.go:800

	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")
		tagParts := strings.Split(tagVersion, ".")
		if len(tagParts) != 3 {

View on GitHub (pinned to ad32144c42)

Solutions

  1. curl -si https://api.github.com/repos/XTLS/Xray-core/releases from the host to see the raw status and body
  2. Fix the panel's proxy settings (Settings → proxy) so the API is reachable, or bypass it
  3. Retry during a GitHub incident after checking githubstatus.com
Defensive patterns

Strategy: try-catch

Try / catch

versions, err := svc.GetXrayVersions()
if err != nil {
    var statusErr = err // matches "GitHub API returned status"
    if strings.Contains(statusErr.Error(), "returned status 5") {
        return retryable(err) // 5xx is transient
    }
    return fatal(err)
}

Prevention

When it happens

Trigger: An intercepting proxy answers api.github.com with a plain 502/503 HTML page; GitHub outage returning 5xx without a JSON envelope; a captive portal returning 302-followed HTML.

Common situations: Panels behind corporate proxies or in regions where api.github.com is filtered; transient GitHub incidents; misconfigured outbound proxy for the panel.

Related errors


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/17ae5b1c498031a7. Report an issue: GitHub.