MHSanaei/3x-ui · error
xray checksum: malformed SHA2-256 entry in digest
Error message
xray checksum: malformed SHA2-256 entry in digest
What it means
Returned by parseXrayDigestSHA256 when a line prefixed 'SHA2-256=' exists but the remaining hex value (after trim + lowercase) is not exactly 64 characters. The .dgst format is 'ALGO= <hex>'; a wrong-length value means the file format changed or the content is corrupted. Hard-coded expectation: SHA-256 hex is always 64 chars.
Source
Thrown at internal/web/service/server.go:980
}
raw, err := io.ReadAll(io.LimitReader(resp.Body, maxXrayDigestBytes))
if err != nil {
return "", fmt.Errorf("download xray checksum: %w", err)
}
return parseXrayDigestSHA256(raw)
}
// parseXrayDigestSHA256 extracts the lowercase SHA2-256 hex from an XTLS .dgst
// file, whose lines are "ALGO= <hex>" (the relevant one being "SHA2-256= ...").
func parseXrayDigestSHA256(dgst []byte) (string, error) {
for line := range strings.SplitSeq(string(dgst), "\n") {
rest, ok := strings.CutPrefix(strings.TrimSpace(line), "SHA2-256=")
if !ok {
continue
}
h := strings.ToLower(strings.TrimSpace(rest))
if len(h) != 64 {
return "", fmt.Errorf("xray checksum: malformed SHA2-256 entry in digest")
}
return h, nil
}
return "", fmt.Errorf("xray checksum: no SHA2-256 entry in digest")
}
func (s *ServerService) UpdateXray(version string) error {
versions, err := s.GetXrayVersions()
if err != nil {
return err
}
if !slices.Contains(versions, version) {
return fmt.Errorf("xray version %q is not in the fetched release list", version)
}
// 1. Stop xray before doing anything
if err := s.StopXrayService(); err != nil {
logger.Warning("failed to stop xray before update:", err)View on GitHub (pinned to ad32144c42)
Solutions
- curl the .dgst URL and inspect the SHA2-256 line's actual shape
- Update the panel to a version matching the current .dgst format, or patch parseXrayDigestSHA256 for the new layout
- If the body looks mangled, retry without the proxy
Example fix
// before
h := strings.ToLower(strings.TrimSpace(rest))
if len(h) != 64 {
return "", fmt.Errorf("xray checksum: malformed SHA2-256 entry in digest")
}
// after — tolerate 'hash filename' lines
if h, _, ok := strings.Cut(rest, " "); ok {
h = h
}
h = strings.ToLower(strings.TrimSpace(strings.Fields(rest)[0]))
if len(h) != 64 {
return "", fmt.Errorf("xray checksum: malformed SHA2-256 entry in digest")
}
Defensive patterns
Strategy: validation
Validate before calling
h := strings.ToLower(strings.TrimSpace(rest))
if !regexp.MustCompile(`^[0-9a-f]{64}$`).MatchString(h) {
return fmt.Errorf("digest entry not valid SHA-256 hex: %q", rest)
}
Type guard
func isValidSHA256Hex(s string) bool {
if len(s) != 64 {
return false
}
for _, c := range s {
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
return false
}
}
return true
}
Prevention
- Validate digest shape with a hex regex before comparing
- Log the offending line when parse guards fire — format drift vs corruption is distinguishable at a glance
- Track upstream release-tooling changes when maintaining a parser for their artifacts
When it happens
Trigger: XTLS changes the .dgst line format (e.g. 'SHA2-256= file' multi-entry lines); a corrupted/truncated sidecar that keeps the prefix but mangles the hash; a proxy-rewritten .dgst body.
Common situations: Future upstream format drift breaking older panels; content-mangling middleboxes.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- xray checksum: no SHA2-256 entry in digest
- Xray update aborted: the downloaded archive does not match t
- download xray checksum: unexpected HTTP %d
- unsupported link scheme
- vmess decode: %w
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/eb4e6d63865c4607.
Report an issue: GitHub.