{"record":{"id":"fc4b64c8286b2faf","repo":"fish2018/pansou","slug":"s-v-w","errorCode":null,"errorMessage":"[%s] 搜索失败: %v；刷新接口标识失败: %w","messagePattern":"\\[(.+?)\\] 搜索失败: (.+?)；刷新接口标识失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/xiaokupan/xiaokupan.go","lineNumber":99,"sourceCode":"func (p *XiaokupanPlugin) SearchWithResult(keyword string, ext map[string]interface{}) (model.PluginSearchResult, error) {\n\treturn p.AsyncSearchWithResult(keyword, p.searchImpl, p.MainCacheKey, ext)\n}\n\nfunc (p *XiaokupanPlugin) searchImpl(client *http.Client, keyword string, _ map[string]interface{}) ([]model.SearchResult, error) {\n\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 {","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/xiaokupan/xiaokupan.go#L81-L117","documentation":"This error is returned by XiaokupanPlugin.searchImpl when the initial search against xiaokupan.com's TanStack server function endpoint fails AND the automatic recovery path (re-discovering the server function ID from the site's homepage and entry JS bundle) also fails. It wraps the original search error (%v) and the refresh error (%w) so both root causes are visible in one message.","triggerScenarios":"searchWithFunctionID returns an error for the cached functionID (network failure, HTTP non-200, stale/discontinued function ID hash, oversized response, parse failure), and then refreshServerFunctionID -> discoverServerFunctionID also errors (homepage unreachable, entry script pattern no longer matches, hash not found, or the same network/HTTP problems).","commonSituations":"xiaokupan.com redeployed its frontend so the baked-in defaultServerFunctionID (sha hash) is stale; the site is down or behind a firewall/proxy; the HTML layout changed so the regex /assets/index-*.js no longer matches; transient DNS/timeouts affecting both requests.","solutions":["Check network connectivity/DNS to xiaokupan.com (curl https://xiaokupan.com/) — most often both errors share the same network root cause","Inspect the wrapped refresh error to see whether discovery failed at homepage fetch, asset lookup, or hash extraction","If the site redeployed, verify the /assets/index-*.js regex and hash extraction logic in discoverServerFunctionID still match the live bundle","Retry after a short delay; if the site is under maintenance the error is transient","Update defaultServerFunctionID to the hash currently embedded in the site's entry JS as a stopgap"],"exampleFix":"// before: opaque chained failure\nreturn nil, fmt.Errorf(\"[%s] 搜索失败: %v；刷新接口标识失败: %w\", p.Name(), err, refreshErr)\n// after: surface both causes explicitly and retry once before giving up\nif refreshErr != nil {\n    return nil, fmt.Errorf(\"xiaokupan: search failed: %v; function-id refresh failed: %w\", err, refreshErr)\n}","handlingStrategy":"fallback","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    var wrapped interface{ Unwrap() error }\n    log.Printf(\"xiaokupan search and function-id refresh both failed: %v\", err) // inspect %v part for original cause\n    return fallbackOtherPlugins(keyword)\n}","preventionTips":["Monitor reachability of xiaokupan.com with a periodic health check","Pin and periodically re-verify defaultServerFunctionID against the live site bundle","Alert on this dual-failure message since it means both primary and recovery paths are broken"],"tags":["network","search","http","error-wrapping"],"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"}