{"record":{"id":"4362eb7d6ae58cdd","repo":"fish2018/pansou","slug":"s-w-4362eb","errorCode":null,"errorMessage":"[%s] 搜索失败: %w","messagePattern":"\\[(.+?)\\] 搜索失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/xiaokupan/xiaokupan.go","lineNumber":104,"sourceCode":"\tkeyword = cleanText(keyword)\n\tif keyword == \"\" {\n\t\treturn []model.SearchResult{}, nil\n\t}\n\tif client == nil {\n\t\tclient = http.DefaultClient\n\t}\n\n\tfunctionID := p.currentServerFunctionID()\n\tresults, err := p.searchWithFunctionID(client, keyword, functionID)\n\tif err != nil {\n\t\trefreshedID, refreshErr := p.refreshServerFunctionID(client, functionID)\n\t\tif refreshErr != nil {\n\t\t\treturn nil, fmt.Errorf(\"[%s] 搜索失败: %v；刷新接口标识失败: %w\", p.Name(), err, refreshErr)\n\t\t}\n\t\tresults, err = p.searchWithFunctionID(client, keyword, refreshedID)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索失败: %w\", p.Name(), err)\n\t}\n\treturn plugin.FilterResultsByKeyword(results, keyword), nil\n}\n\nfunc (p *XiaokupanPlugin) searchWithFunctionID(client *http.Client, keyword, functionID string) ([]model.SearchResult, error) {\n\tpayload, err := buildSearchPayload(keyword)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"构造搜索参数失败: %w\", err)\n\t}\n\n\tendpoint := fmt.Sprintf(\"%s/_serverFn/%s\", strings.TrimRight(p.baseURL, \"/\"), functionID)\n\tparsedEndpoint, err := url.Parse(endpoint)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"解析搜索地址失败: %w\", err)\n\t}\n\tquery := parsedEndpoint.Query()\n\tquery.Set(\"payload\", string(payload))\n\tparsedEndpoint.RawQuery = query.Encode()","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/xiaokupan/xiaokupan.go#L86-L122","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"func canReachXiaokupan() error {\n    resp, err := http.Head(\"https://xiaokupan.com/\")\n    if err != nil { return err }\n    defer resp.Body.Close()\n    if resp.StatusCode != http.StatusOK { return fmt.Errorf(\"homepage HTTP %d\", resp.StatusCode) }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"results, err := plugin.SearchWithResult(keyword, ext)\nif err != nil {\n    // %w chain reveals root cause: 请求失败/HTTP NNN/解析... failure\n    log.Printf(\"xiaokupan search failed: %v\", err)\n    time.Sleep(2 * time.Second)\n    results, err = plugin.SearchWithResult(keyword, ext) // one retry\n    if err != nil { return otherSources(keyword) }\n}","preventionTips":["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"],"tags":["network","search","http"],"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"}