fish2018/pansou · error
[ ] 获取详情失败
Error message
[%s] 获取详情失败: %s
What it means
This is the fallback error returned by getDetailInfo when every candidate detail URL failed but no specific lastErr was recorded — i.e. the candidate list was empty (or all iterations somehow set no error), so the plugin has no underlying cause to report. It simply states that detail information could not be obtained for the given URL.
Solutions
- Log the detailURL passed in and check what p.detailURLCandidates returns for it — likely zero candidates for this input format.
- Validate/normalize detailURL before calling buildResult/getDetailInfo (correct scheme, host, non-empty).
- Make the loop set a default error even when candidates exist but produce none, or guard against an empty candidate list early with a clearer message.
- If the URL is valid, compare against the URL shapes detailURLCandidates expects and extend it to handle this URL form.
Example fix
// before
if lastErr == nil {
lastErr = fmt.Errorf("[%s] 获取详情失败: %s", p.Name(), detailURL)
}
// after
candidates := p.detailURLCandidates(detailURL)
if len(candidates) == 0 {
return detailInfo{}, fmt.Errorf("[%s] 无可用候选地址: %s", p.Name(), detailURL)
}
if lastErr == nil {
lastErr = fmt.Errorf("[%s] 获取详情失败: %s", p.Name(), detailURL)
} Defensive patterns
Strategy: validation
Validate before calling
if detailURL == "" || !strings.HasPrefix(detailURL, "http") {
return errors.New("invalid detail URL: must be a non-empty absolute http(s) URL")
} Type guard
func isValidDetailURL(u string) bool {
parsed, err := neturl.Parse(u)
return err == nil && (parsed.Scheme == "http" || parsed.Scheme == "https") && parsed.Host != ""
} Try / catch
info, err := plugin.GetDetailInfo(client, detailURL, title, pic, false)
if err != nil {
if strings.Contains(err.Error(), "获取详情失败") {
// no usable candidate; skip this item rather than failing the batch
logger.Warnf("skipping %s: %v", detailURL, err)
return nil
}
return err
} Prevention
- Validate/normalize detail URLs before calling buildResult.
- Check that detailURLCandidates always yields at least one candidate for accepted URL shapes.
- Unit-test detailURLCandidates against all URL variants you feed the plugin.
- Never pass empty or relative URLs into the detail pipeline.
When it happens
Trigger: getDetailInfo exhausts p.detailURLCandidates(detailURL) with lastErr still nil — practically, when detailURLCandidates returns an empty slice for the input detailURL (e.g. an unrecognized/malformed URL yielding no candidates), and the cache holds no valid entry.
Common situations: Passing a detail URL in an unexpected format or from a different domain so the candidate generator produces nothing; calling with an empty string URL; a refactor removed the default candidate while keeping the loop; forceRefresh cleared a previously cached entry and then no candidates exist.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/d4d41cebe87366a4.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/qiwei/qiwei.go:343
}
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
}
// solveVerification completes the site's deterministic slider challenge. The
// challenge is session-bound, so the caller and this method must share a
// cookie jar on the same http.Client.
func (p *QiweiPlugin) solveVerification(client *http.Client, pageURL, verifyHTML string) error {
scriptMatch := verificationScriptRegex.FindStringSubmatch(verifyHTML)
if len(scriptMatch) < 2 {
return fmt.Errorf("未找到滑动验证脚本")
}
scriptURL := normalizeURL(pageURL, scriptMatch[1])
jsBody, err := p.fetchBody(client, scriptURL, pageURL, detailTimeout)
if err != nil {
return fmt.Errorf("获取验证脚本失败: %w", err)
}
View on GitHub (pinned to beaa561337)