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

  1. Look at the wrapped cause (%w chain): 请求失败 = network, HTTP NNN = status, 解析/读取 errors = transport, 解析 Seroval 响应失败 = upstream changed response shape
  2. Curl the endpoint manually: https://xiaokupan.com/_serverFn/<functionID>?payload=... to see what the server returns
  3. If HTTP 404/400, the function ID is stale — confirm refreshServerFunctionID can discover a new one from the site's entry JS
  4. If the Seroval shape changed, update parseSearchResponse to match the new response structure
  5. 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

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)