fish2018/pansou · error
解析验证数据失败
Error message
解析验证数据失败: %w
What it means
When the plugin receives an anti-bot challenge page, it extracts an embedded JSON payload (matches[1]) with the challenge parameters and unmarshals it into ChallengePageData. If json.Unmarshal fails — the extracted blob is not valid JSON or doesn't match the expected shape (e.g. it's HTML-wrapped or the site changed its page format) — the error is wrapped as '解析验证数据失败: %w'.
Solutions
- Log the wrapped %w error plus matches[1] (truncated) to see the actual payload, then update the extraction regex/decoding (e.g. html.UnescapeString) for the new page format.
- Sanitize the blob before unmarshaling: strip surrounding quotes/backslashes (if JSON-in-string, json.Unmarshal into a string first) and trim non-JSON trailing characters.
- Check if the page is actually a different challenge type and route it to solveRemotePowChallenge or another solver instead of parsing it as POW data.
- Update ChallengePageData field tags if the site renamed fields, so unmarshal succeeds.
Example fix
// before
if err := json.Unmarshal(matches[1], &challenge); err != nil {
return fmt.Errorf("解析验证数据失败: %w", err)
}
// after
blob := []byte(html.UnescapeString(string(matches[1])))
if err := json.Unmarshal(blob, &challenge); err != nil {
return p.solveRemotePowChallenge(scraper, requestURL) // fall back for unexpected formats
} Defensive patterns
Strategy: fallback
Validate before calling
func looksLikeChallengeJSON(s string) bool {
var c struct { ID string `json:"id"`; N string `json:"n"`; X string `json:"x"`; T int64 `json:"t"` }
return json.Unmarshal([]byte(s), &c) == nil && c.ID != "" && c.N != ""
}
// if false, skip POW parsing and use the remote-challenge path Try / catch
challenge, err := parseChallengeFromBody(body)
if err != nil {
if strings.Contains(err.Error(), "解析验证数据失败") {
return p.solveRemotePowChallenge(scraper, requestURL) // graceful fallback
}
return err
} Prevention
- HTML-unescape and trim the extracted payload before json.Unmarshal.
- Log a truncated sample of the raw page whenever parsing fails to catch upstream format changes early.
- Add a regression test with a saved copy of a real challenge page to detect upstream format changes.
- Prefer extracting JSON via a quoted-string decode (unmarshal into string first) instead of raw regex capture when the payload is JSON-in-HTML.
When it happens
Trigger: The regex matched a challenge script on the page but the captured group contains malformed or non-canonical JSON (HTML entities, trailing garbage, new site page layout), so json.Unmarshal into ChallengePageData errors before the POW solver can run.
Common situations: Upstream site updated its challenge page format so the regex grabs the wrong region; response decompressed/escaped differently than expected; site returns a different captcha variant whose payload isn't the expected JSON object.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/1ec6882568b4ec65.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/gying/gying.go:1665
baseTransport.Proxy = http.ProxyURL(proxyURL)
}
if DebugLog {
fmt.Printf("[Gying] 已应用代理到scraper: %s\n", config.AppConfig.ProxyURL)
}
return nil
}
func (p *GyingPlugin) solveBotChallenge(scraper *cloudscraper.Scraper, requestURL string, body []byte) error {
matches := challengeJSONPattern.FindSubmatch(body)
if len(matches) < 2 {
return p.solveRemotePowChallenge(scraper, requestURL)
}
var challenge ChallengePageData
if err := json.Unmarshal(matches[1], &challenge); err != nil {
return fmt.Errorf("解析验证数据失败: %w", err)
}
if challenge.ID != "" && challenge.N != "" && challenge.X != "" && challenge.T > 0 {
return p.solvePowChallenge(scraper, requestURL, &challenge)
}
return p.solveLegacyHashChallenge(scraper, requestURL, &challenge)
}
func (p *GyingPlugin) solveRemotePowChallenge(scraper *cloudscraper.Scraper, requestURL string) error {
powURL, err := p.buildPowURL(requestURL)
if err != nil {
return err
}
if DebugLog {
fmt.Printf("[Gying] Remote PoW Challenge命中: url=%s powURL=%s\n", requestURL, powURL)
}View on GitHub (pinned to beaa561337)