fish2018/pansou · error
验证数据无效
Error message
验证数据无效
What it means
solveLegacyHashChallenge validates the legacy hash-cash style challenge before brute-forcing SHA-256 nonces: it needs an ID, a salt, a positive Diff (nonce upper bound), and at least one target hash. This error is returned when any of those is missing or invalid, meaning the challenge page data does not match the expected legacy format.
Solutions
- Dump the raw challenge response and the populated ChallengePageData to see which field is missing
- Re-fetch a fresh challenge; transient empty responses are common behind proxies
- If the site now serves PoW challenges, route to solvePowChallenge instead of the legacy solver
- Update ChallengePageData JSON tags to match the current challenge schema
Example fix
// before
if challenge.ID == "" || challenge.Salt == "" || challenge.Diff <= 0 || len(challenge.Challenge) == 0 {
return fmt.Errorf("验证数据无效")
}
// after (log which field failed)
if challenge.ID == "" || challenge.Salt == "" || challenge.Diff <= 0 || len(challenge.Challenge) == 0 {
return fmt.Errorf("验证数据无效: id=%q salt=%q diff=%d targets=%d",
challenge.ID, challenge.Salt, challenge.Diff, len(challenge.Challenge))
} Defensive patterns
Strategy: validation
Validate before calling
func validLegacyChallenge(c *ChallengePageData) bool {
return c.ID != "" && c.Salt != "" && c.Diff > 0 && len(c.Challenge) > 0
}
if !validLegacyChallenge(ch) { /* route to PoW solver or re-fetch */ } Type guard
func isLegacyHashChallenge(c *ChallengePageData) bool {
return c.ID != "" && c.Salt != "" && c.Diff > 0 && len(c.Challenge) > 0
} Try / catch
if err := p.solveLegacyHashChallenge(scraper, requestURL, challenge); err != nil {
if err.Error() == "验证数据无效" {
return p.solvePowChallenge(scraper, requestURL, challenge) // fallback scheme
}
return err
} Prevention
- Detect which challenge scheme the server issued before dispatching to a solver
- Log raw challenge responses so missing Salt/Diff/Challenge is visible immediately
- Re-fetch the challenge page rather than solving a partially-populated struct
When it happens
Trigger: The solver is dispatched to solveLegacyHashChallenge with a ChallengePageData where ID=="", Salt=="", Diff<=0, or Challenge (target hash list) is empty.
Common situations: The site migrated from the legacy hash challenge to the PoW scheme but the dispatcher still routes to the legacy solver; JSON field renaming upstream leaves Salt/Diff/Challenge at zero values; an empty or malformed challenge response was parsed without a hard failure.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/619c7b90ff67ba74.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/gying/gying.go:1777
y.Mod(y, modulus)
}
elapsed := time.Since(start)
if DebugLog {
fmt.Printf("[Gying] PoW Challenge计算完成: id=%s t=%d cost=%s\n",
challenge.ID, challenge.T, elapsed.Round(time.Millisecond))
}
if minSolveTime := 3 * time.Second; elapsed < minSolveTime {
time.Sleep(minSolveTime - elapsed)
}
return y.Text(16), nil
}
func (p *GyingPlugin) solveLegacyHashChallenge(scraper *cloudscraper.Scraper, requestURL string, challenge *ChallengePageData) error {
if challenge.ID == "" || challenge.Salt == "" || challenge.Diff <= 0 || len(challenge.Challenge) == 0 {
return fmt.Errorf("验证数据无效")
}
if DebugLog {
fmt.Printf("[Gying] Challenge命中: url=%s id=%s diff=%d targets=%d\n",
requestURL, challenge.ID, challenge.Diff, len(challenge.Challenge))
}
remaining := make(map[string][]int, len(challenge.Challenge))
nonces := make([]int, len(challenge.Challenge))
for idx, target := range challenge.Challenge {
hash := strings.ToLower(target)
remaining[hash] = append(remaining[hash], idx)
}
workerCount := runtime.GOMAXPROCS(0)
if workerCount < 1 {
workerCount = 1
}View on GitHub (pinned to beaa561337)