{"record":{"id":"b4ea45e1907bc7ba","repo":"fish2018/pansou","slug":"s-w-b4ea45","errorCode":null,"errorMessage":"[%s] 详情页验证失败: %w","messagePattern":"\\[(.+?)\\] 详情页验证失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"plugin/qiwei/qiwei.go","lineNumber":318,"sourceCode":"\t} else if cached, ok := p.detailCache.Load(detailURL); ok {\n\t\tif entry, ok := cached.(detailCacheEntry); ok {\n\t\t\tif time.Since(entry.CachedAt) < cacheTTL {\n\t\t\t\treturn entry.Info, nil\n\t\t\t}\n\t\t\tp.detailCache.Delete(detailURL)\n\t\t}\n\t}\n\n\tvar lastErr error\n\tfor _, candidateURL := range p.detailURLCandidates(detailURL) {\n\t\tbody, err := p.fetchBody(client, candidateURL, candidateURL, detailTimeout)\n\t\tif err != nil {\n\t\t\tlastErr = err\n\t\t\tcontinue\n\t\t}\n\t\tif isVerifyPage(body) {\n\t\t\tif verifyErr := p.solveVerification(client, candidateURL, body); verifyErr != nil {\n\t\t\t\tlastErr = fmt.Errorf(\"[%s] 详情页验证失败: %w\", p.Name(), verifyErr)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tbody, err = p.fetchBody(client, candidateURL, candidateURL, detailTimeout)\n\t\t\tif err != nil {\n\t\t\t\tlastErr = err\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif isVerifyPage(body) {\n\t\t\t\tlastErr = fmt.Errorf(\"[%s] 详情页验证未通过: %s\", p.Name(), candidateURL)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tinfo, err := p.parseDetail(candidateURL, body, fallbackTitle, fallbackPic)\n\t\tif err != nil {\n\t\t\tlastErr = err\n\t\t\tcontinue\n\t\t}","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/qiwei/qiwei.go#L300-L336","documentation":"This error wraps a failure that occurred while the plugin tried to solve the site's slider (CAPTCHA) verification challenge after the detail page fetch returned a verification page. It is produced inside getDetailInfo when solveVerification returns an error for a candidate URL, and the wrapped cause (e.g. missing verification script, script fetch failure, incomplete script parameters) is carried via %w. It means the plugin could not automatically pass the anti-bot challenge for that candidate URL, so it moves on to the next candidate and ultimately reports this as lastErr if all candidates fail.","triggerScenarios":"Calling buildResult/getDetailInfo when the target detail page responds with a verification page (detected by isVerifyPage) and solveVerification fails — e.g. the verification script tag does not match verificationScriptRegex, downloading the JS script fails, or the script contents lack the expected type/key/value parameters.","commonSituations":"The site changed its verification page markup or script URL so the regexes no longer match; the site hardened its CAPTCHA (session-bound challenge with rotating tokens); requests are being rate-limited or flagged as bot traffic; a proxy/CDN serves an interstitial challenge page to the scraper's datacenter IP.","solutions":["Retry later or from a different IP — the challenge is often triggered by rate limiting or flagged IPs; reduce request frequency.","Check whether the verification page markup changed and update verificationScriptRegex and the other verification*Regex patterns in plugin/qiwei/qiwei.go.","Ensure the same *http.Client with a shared cookie jar is used across fetchBody and solveVerification, since the challenge is session-bound.","Inspect the wrapped cause (errors.Unwrap / %v of the error) to see whether it was 未找到滑动验证脚本, 获取验证脚本失败, or 验证脚本参数不完整 and fix that specific step.","Log the verifyHTML body to confirm isVerifyPage is not producing false positives on a normal page."],"exampleFix":"// before\nlastErr = fmt.Errorf(\"[%s] 详情页验证失败: %w\", p.Name(), verifyErr)\ncontinue\n// after\n// inspect the wrapped cause to react specifically\nif errors.Is(verifyErr, errVerificationScriptNotFound) {\n    logger.Warnf(\"%s: verification script missing, page markup may have changed\", p.Name())\n}\nlastErr = fmt.Errorf(\"[%s] 详情页验证失败: %w\", p.Name(), verifyErr)\ncontinue","handlingStrategy":"retry","validationCode":"// probe before parsing\ndetailTimeout := 15 * time.Second\nbody, err := fetchBody(client, url, url, detailTimeout)\nif err == nil && isVerifyPage(body) {\n    if err := plugin.SolveVerification(client, url, body); err != nil {\n        logger.Warnf(\"verification unsolvable for %s: %v\", url, err)\n    }\n}","typeGuard":"func isWrappedVerificationErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"详情页验证失败\")\n}","tryCatchPattern":"info, err := plugin.GetDetailInfo(client, detailURL, title, pic, false)\nif err != nil {\n    var wrapped error\n    if errors.As(err, &wrapped) && strings.Contains(err.Error(), \"详情页验证失败\") {\n        // fall back to cached/fallback title+cover, or queue for retry with backoff\n        return fallbackResult, nil\n    }\n    return nil, err\n}","preventionTips":["Throttle requests and add jitter to avoid triggering the site's CAPTCHA in the first place.","Use a session-persistent cookie jar on one http.Client for the whole detail flow.","Alert on rate of verification-page responses — a spike means your IP is flagged.","Keep the verification*Regex patterns covered by tests against saved page fixtures.","Consider residential/proxy rotation when datacenter IPs are persistently challenged."],"tags":["captcha","anti-bot","scraping","verification","network"],"backgroundTag":"upstream-api-error","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}