fish2018/pansou · error
[ ] 搜索失败
Error message
[%s] 搜索失败: %w
What it means
Returned by XiaokupanPlugin.searchImpl when the search against xiaokupan.com fails after the automatic function-ID refresh path has been exhausted (or the refresh succeeded but the retry with the fresh ID also failed). It wraps the final search error with %w and prefixes the plugin name.
Solutions
- Look at the wrapped cause (%w chain): 请求失败 = network, HTTP NNN = status, 解析/读取 errors = transport, 解析 Seroval 响应失败 = upstream changed response shape
- Curl the endpoint manually: https://xiaokupan.com/_serverFn/<functionID>?payload=... to see what the server returns
- If HTTP 404/400, the function ID is stale — confirm refreshServerFunctionID can discover a new one from the site's entry JS
- If the Seroval shape changed, update parseSearchResponse to match the new response structure
- Retry later for transient 5xx/timeout cases
Defensive patterns
Strategy: retry
Validate before calling
func canReachXiaokupan() error {
resp, err := http.Head("https://xiaokupan.com/")
if err != nil { return err }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { return fmt.Errorf("homepage HTTP %d", resp.StatusCode) }
return nil
} Try / catch
results, err := plugin.SearchWithResult(keyword, ext)
if err != nil {
// %w chain reveals root cause: 请求失败/HTTP NNN/解析... failure
log.Printf("xiaokupan search failed: %v", err)
time.Sleep(2 * time.Second)
results, err = plugin.SearchWithResult(keyword, ext) // one retry
if err != nil { return otherSources(keyword) }
} Prevention
- Keep defaultServerFunctionID current with the site's deployed bundle
- Handle the specific wrapped causes (404 → stale ID, 5xx → wait, timeout → network) differently
- Cache successful results so repeated searches don't re-hit the flaky upstream
When it happens
Trigger: Either the first searchWithFunctionID attempt failed, refresh succeeded, and the retry with the refreshed ID also failed — or the first attempt failed and the refreshed-ID retry itself errored. Root causes come from searchWithFunctionID: payload construction, URL parse, request creation, doLimitedRequest (network/HTTP/size), or parseSearchResponse (Seroval decode).
Common situations: Upstream site temporarily down or returning 5xx; stale server-function hash even after refresh (site changed its bundle layout); response exceeds the 4MB maxSearchResponseSize cap; keyword produces a payload the endpoint rejects.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/4362eb7d6ae58cdd.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/xiaokupan/xiaokupan.go:104
keyword = cleanText(keyword)
if keyword == "" {
return []model.SearchResult{}, nil
}
if client == nil {
client = http.DefaultClient
}
functionID := p.currentServerFunctionID()
results, err := p.searchWithFunctionID(client, keyword, functionID)
if err != nil {
refreshedID, refreshErr := p.refreshServerFunctionID(client, functionID)
if refreshErr != nil {
return nil, fmt.Errorf("[%s] 搜索失败: %v;刷新接口标识失败: %w", p.Name(), err, refreshErr)
}
results, err = p.searchWithFunctionID(client, keyword, refreshedID)
}
if err != nil {
return nil, fmt.Errorf("[%s] 搜索失败: %w", p.Name(), err)
}
return plugin.FilterResultsByKeyword(results, keyword), nil
}
func (p *XiaokupanPlugin) searchWithFunctionID(client *http.Client, keyword, functionID string) ([]model.SearchResult, error) {
payload, err := buildSearchPayload(keyword)
if err != nil {
return nil, fmt.Errorf("构造搜索参数失败: %w", err)
}
endpoint := fmt.Sprintf("%s/_serverFn/%s", strings.TrimRight(p.baseURL, "/"), functionID)
parsedEndpoint, err := url.Parse(endpoint)
if err != nil {
return nil, fmt.Errorf("解析搜索地址失败: %w", err)
}
query := parsedEndpoint.Query()
query.Set("payload", string(payload))
parsedEndpoint.RawQuery = query.Encode()View on GitHub (pinned to beaa561337)