fish2018/pansou · warning

无效十六进制转义

Error message

无效十六进制转义: %w

What it means

decodeHexEscape calls strconv.ParseUint on the escaped hex digits; when those bytes are not valid hexadecimal, it wraps the parse error as 无效十六进制转义 (invalid hex escape). This indicates the plugin's JS string decoder encountered an escape like \xZZ or a slice containing non-hex characters.

Solutions

  1. Dump the failing slice (data[start:end]) to see what characters broke the parse
  2. Extend decodeHexEscape to handle the site's current escape syntax (e.g. \uXXXX, variable-length escapes)
  3. Check whether the page needs charset conversion (GBK/UTF-8) before decoding
  4. Skip items that fail to decode, using cleanText(rawTitle) as a fallback

Example fix

// before
value, err := strconv.ParseUint(string(data[start:end]), 16, 16)
if err != nil {
    return 0, start, fmt.Errorf("无效十六进制转义: %w", err)
}
// after
value, err := strconv.ParseUint(string(data[start:end]), 16, 16)
if err != nil {
    return 0, start, fmt.Errorf("无效十六进制转义 at %d (%q): %w", start, data[start:end], err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: verify all escape bytes are hex before decoding
for _, b := range data[start : start+length] {
    if !isHexDigit(b) {
        return string(data), nil // not a valid escape, use raw
    }
}

Type guard

func isHexDigit(b byte) bool {
    return (b >= '0' && b <= '9') || (b >= 'a' && b <= 'f') || (b >= 'A' && b <= 'F')
}

Try / catch

value, n, err := decodeHexEscape(data, i+2, 2)
if err != nil {
    log.Printf("invalid hex escape at %d: %v", i, err)
    return string(data), nil // keep raw title on decode failure
}

Prevention

When it happens

Trigger: decodeJSSingleQuotedString encounters a \x or multi-digit escape whose following bytes fail strconv.ParseUint(...,16,16): non-hex characters, or an empty/garbled slice passed as the escape.

Common situations: Upstream site changes obfuscation scheme (e.g. switches to \u{...} or raw Unicode), so bytes assumed to be hex digits are no longer valid; or page compression/decoding leaves stray bytes inside JS string literals.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/754ae0c9439e34a7. Report an issue: GitHub.

Appendix: source

Thrown at plugin/haitunsou/haitunsou.go:258

					index = lowNext - 1
				}
			}
			decoded = utf8.AppendRune(decoded, r)
		default:
			decoded = append(decoded, encoded[index])
		}
	}
	return decoded, nil
}

func decodeHexEscape(data []byte, start, length int) (uint64, int, error) {
	end := start + length
	if start < 0 || end > len(data) {
		return 0, start, fmt.Errorf("转义序列不完整")
	}
	value, err := strconv.ParseUint(string(data[start:end]), 16, 16)
	if err != nil {
		return 0, start, fmt.Errorf("无效十六进制转义: %w", err)
	}
	return value, end, nil
}

func convertItem(item searchItem) (model.SearchResult, bool) {
	rawTitle := cleanText(item.Title)
	if rawTitle == "" {
		rawTitle = cleanText(item.Name)
	}
	if rawTitle == "" {
		return model.SearchResult{}, false
	}
	title, description := splitTitleAndDescription(rawTitle)
	if title == "" {
		return model.SearchResult{}, false
	}

	linkType, normalizedURL, password := normalizeLink(item.URL, item.Code, item.IsType)

View on GitHub (pinned to beaa561337)