fish2018/pansou · warning

[ ] 详情页验证未通过

Error message

[%s] 详情页验证未通过: %s

What it means

This error means the plugin solved the slider verification (or attempted to), re-fetched the detail page, but the response was STILL a verification page (isVerifyPage(body) true after solving). It indicates the automated challenge bypass did not convince the server, so the candidate URL is abandoned and this is recorded as lastErr for getDetailInfo.

Solutions

  1. Verify the same *http.Client (with its cookie jar) is reused for solveVerification and the re-fetch — a fresh client loses the session cookie that marks the challenge as solved.
  2. Capture and replay the verification request in a browser (DevTools) to confirm the expected value encoding (md5StringToHex) and endpoint path still match; update verification*Regex / endpoint logic if the site changed.
  3. Slow down request rate and add jitter/backoff — persistent challenges usually mean the IP/session is flagged.
  4. Increase candidate coverage by checking p.detailURLCandidates so another candidate URL may succeed.
  5. Manually inspect the re-fetched body: if it is a different challenge type, the deterministic slider solver no longer applies and needs reimplementation.

Example fix

// before
client := &http.Client{Timeout: detailTimeout} // new client each call, loses cookies
// after
client := &http.Client{Timeout: detailTimeout, Jar: sharedCookieJar} // reuse jar across solveVerification + re-fetch
Defensive patterns

Strategy: fallback

Validate before calling

// verify the session actually retained the solve cookie before refetching
if len(client.Jar.Cookies(parsedURL)) == 0 {
    return errors.New("cookie jar empty; verification session cannot persist")
}

Type guard

func isVerifyNotPassedErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "详情页验证未通过")
}

Try / catch

info, err := plugin.GetDetailInfo(client, detailURL, title, pic, false)
if isVerifyNotPassedErr(err) {
    // drop to fallback data (base title/cover) and mark URL for a later retry
    result.MarkNeedsRetry(detailURL)
    return fallbackResult, nil
}

Prevention

When it happens

Trigger: In getDetailInfo, after solveVerification succeeds, the follow-up p.fetchBody of candidateURL returns HTML that still matches isVerifyPage — the server re-issues the challenge for that session/candidate URL.

Common situations: The site invalidates the verification cookie server-side or issues a fresh challenge per request; the md5-encoded value sent to the verification endpoint is rejected (site changed hashing or parameter names); heavy request volume keeps the session flagged; cookies are not persisted because a new client/cookie jar was used between solve and re-fetch.

Related errors


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

Appendix: source

Thrown at plugin/qiwei/qiwei.go:327

	var lastErr error
	for _, candidateURL := range p.detailURLCandidates(detailURL) {
		body, err := p.fetchBody(client, candidateURL, candidateURL, detailTimeout)
		if err != nil {
			lastErr = err
			continue
		}
		if isVerifyPage(body) {
			if verifyErr := p.solveVerification(client, candidateURL, body); verifyErr != nil {
				lastErr = fmt.Errorf("[%s] 详情页验证失败: %w", p.Name(), verifyErr)
				continue
			}
			body, err = p.fetchBody(client, candidateURL, candidateURL, detailTimeout)
			if err != nil {
				lastErr = err
				continue
			}
			if isVerifyPage(body) {
				lastErr = fmt.Errorf("[%s] 详情页验证未通过: %s", p.Name(), candidateURL)
				continue
			}
		}

		info, err := p.parseDetail(candidateURL, body, fallbackTitle, fallbackPic)
		if err != nil {
			lastErr = err
			continue
		}

		p.detailCache.Store(detailURL, detailCacheEntry{Info: info, CachedAt: time.Now()})
		return info, nil
	}

	if lastErr == nil {
		lastErr = fmt.Errorf("[%s] 获取详情失败: %s", p.Name(), detailURL)
	}
	return detailInfo{}, lastErr

View on GitHub (pinned to beaa561337)