{"record":{"id":"b660c2a1f76f5902","repo":"fish2018/pansou","slug":"s-w-b660c2","errorCode":null,"errorMessage":"[%s] 搜索请求失败: %w","messagePattern":"\\[(.+?)\\] 搜索请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/yingso/yingso.go","lineNumber":227,"sourceCode":"\nfunc (p *YingsoPlugin) search(ctx context.Context, client *http.Client, config apiConfig, keyword string) ([]searchItem, error) {\n\tpayload := searchPayload{\n\t\tPageSize: pageSize,\n\t\tPageNum:  1,\n\t\tTitle:    keyword,\n\t\tRoot:     0,\n\t\tCategory: \"all\",\n\t\tUserID:   config.UserID,\n\t}\n\tencrypted, err := encryptPayload(payload, config)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 生成搜索参数失败: %w\", p.Name(), err)\n\t}\n\n\tvar response apiEnvelope[[]searchItem]\n\tendpoint := fmt.Sprintf(\"%s/%s/search\", p.apiBaseURL, url.PathEscape(config.URLVersion))\n\tif err := p.requestJSON(ctx, client, http.MethodPost, endpoint, encrypted, &response); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tif response.Code != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索接口异常: code=%d msg=%s\", p.Name(), response.Code, response.Msg)\n\t}\n\treturn response.Data, nil\n}\n\nfunc (p *YingsoPlugin) resolveItem(ctx context.Context, client *http.Client, config apiConfig, item searchItem) (model.SearchResult, error) {\n\tpayload := getKeyPayload{ID: item.ID, UserID: config.UserID}\n\tencrypted, err := encryptPayload(payload, config)\n\tif err != nil {\n\t\treturn model.SearchResult{}, err\n\t}\n\n\tvar response apiEnvelope[string]\n\tendpoint := fmt.Sprintf(\"%s/%s/getKey\", p.apiBaseURL, url.PathEscape(config.URLVersion))\n\tif err := p.requestJSON(ctx, client, http.MethodPost, endpoint, encrypted, &response); err != nil {\n\t\treturn model.SearchResult{}, err","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/yingso/yingso.go#L209-L245","documentation":"The encrypted POST to {apiBaseURL}/{URLVersion}/search failed at the transport/request level: requestJSON returned an error (network failure, timeout, non-decodable body, etc.). The plugin wraps the underlying error so callers see it was the search request that failed, distinct from a non-200 search response.","triggerScenarios":"p.requestJSON(ctx, client, http.MethodPost, endpoint, encrypted, &response) returns non-nil error in search, where endpoint = p.apiBaseURL+\"/\"+url.PathEscape(config.URLVersion)+\"/search\".","commonSituations":"Network outage, DNS failure, or proxy blocking the search host; request timeout configured too low; an invalid URLVersion produces a 404 or malformed URL; TLS certificate problems; server 5xx with a non-JSON body that requestJSON cannot decode.","solutions":["Inspect the wrapped error for the root cause (DNS, timeout, TLS, status, decode)","Verify config.URLVersion is current and the versioned /search endpoint exists","Increase the HTTP client timeout or add retries with backoff for transient failures","Check network/proxy connectivity to p.apiBaseURL"],"exampleFix":"// before\nclient := &http.Client{} // no timeout, hangs\n// after\nclient := &http.Client{Timeout: 15 * time.Second} // fail fast, then retry transient errors","handlingStrategy":"retry","validationCode":"u, err := url.Parse(fmt.Sprintf(\"%s/%s/search\", apiBaseURL, url.PathEscape(config.URLVersion)))\nif err != nil || u.Host == \"\" {\n    return fmt.Errorf(\"invalid search endpoint URL\")\n}","typeGuard":null,"tryCatchPattern":"items, err := p.search(ctx, client, cfg, kw)\nif err != nil {\n    var nerr net.Error\n    if errors.As(err, &nerr) && (nerr.Timeout() || isTransient(nerr)) {\n        return retry.WithBackoff(ctx, 3, p.search, ctx, client, cfg, kw)\n    }\n    return err\n}","preventionTips":["Set a sane http.Client timeout and retry transient network errors with backoff","Validate URLVersion before building the endpoint path (PathEscape alone is not enough)","Monitor search endpoint availability and latency","Surface the wrapped root cause to logs instead of swallowing it"],"tags":["network","http","api","search"],"backgroundTag":"http-request-failed","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"}