MHSanaei/3x-ui · error

latest panel release tag is empty

Error message

latest panel release tag is empty

What it means

Returned by fetchLatestPanelVersion when the GitHub releases API call succeeded (HTTP 200, JSON decoded) but the tag_name field is empty. The API contract expects every release object to carry tag_name, so an empty value means the response shape changed, a proxy rewrote the body, or a JSON decode partially matched an unexpected payload. It is a sanity guard before the tag is used for update logic.

Source

Thrown at internal/web/service/panel/panel.go:408

		return "", fmt.Errorf("panel updater download is empty")
	}
	if n > maxPanelUpdaterBytes {
		return "", fmt.Errorf("panel updater exceeds %d bytes", maxPanelUpdaterBytes)
	}
	if err := file.Chmod(0o700); err != nil {
		return "", err
	}
	ok = true
	return path, nil
}

func fetchLatestPanelVersion() (string, error) {
	release, err := fetchPanelRelease("")
	if err != nil {
		return "", err
	}
	if release.TagName == "" {
		return "", fmt.Errorf("latest panel release tag is empty")
	}
	return release.TagName, nil
}

// fetchPanelRelease fetches a release from GitHub. An empty tag resolves the
// latest stable release; a non-empty tag (e.g. dev-latest) resolves that tag.
func fetchPanelRelease(tag string) (*service.Release, error) {
	url := "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest"
	if tag != "" {
		url = "https://api.github.com/repos/MHSanaei/3x-ui/releases/tags/" + tag
	}
	client := (&service.SettingService{}).NewProxiedHTTPClient(10 * time.Second)
	req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
	if reqErr != nil {
		return nil, reqErr
	}
	resp, err := client.Do(req)
	if err != nil {

View on GitHub (pinned to ad32144c42)

Solutions

  1. curl -s https://api.github.com/repos/MHSanaei/3x-ui/releases/latest | jq .tag_name from the same host to see the raw payload
  2. Bypass or reconfigure the outbound proxy so the genuine GitHub API response arrives
  3. Update the panel to the latest version in case the field handling was fixed upstream
Defensive patterns

Strategy: validation

Validate before calling

var release service.Release
if err := json.NewDecoder(resp.Body).Decode(&release); err == nil {
    if release.TagName == "" {
        log.Printf("release payload missing tag_name; body shape changed or proxy rewrote it")
    }
}

Type guard

func hasTagName(r *service.Release) bool {
    return r != nil && r.TagName != ""
}

Prevention

When it happens

Trigger: GET https://api.github.com/repos/MHSanaei/3x-ui/releases/latest returns 200 with a body lacking tag_name; an intercepting proxy returns a 200 JSON status page; a GitHub API change renames the field.

Common situations: Man-in-the-middle / filtering proxies that answer API URLs with their own JSON; using an API mirror with a different schema; running an ancient build against a future GitHub API change.

Related errors


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