{"record":{"id":"0f219976035fab3e","repo":"fish2018/pansou","slug":"s-w-0f2199","errorCode":null,"errorMessage":"[%s] 获取首页失败: %w","messagePattern":"\\[(.+?)\\] 获取首页失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/pansearch/pansearch.go","lineNumber":528,"sourceCode":"\t}\n\n\tfirstPageResults, total, err := p.fetchFirstPage(keyword, baseURL, client)\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"404\") || strings.Contains(err.Error(), \"Not Found\") {\n\t\t\tbuildIdMutex.Lock()\n\t\t\tbuildIdCache = \"\"\n\t\t\tbuildIdCacheTime = time.Time{}\n\t\t\tbuildIdMutex.Unlock()\n\t\t\tbaseURL, err = p.getBaseURL(client)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"[%s] 刷新 buildId 失败: %w\", p.Name(), err)\n\t\t\t}\n\t\t\tfirstPageResults, total, err = p.fetchFirstPage(keyword, baseURL, client)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"[%s] 刷新 buildId 后获取首页失败: %w\", p.Name(), err)\n\t\t\t}\n\t\t} else {\n\t\t\treturn nil, fmt.Errorf(\"[%s] 获取首页失败: %w\", p.Name(), err)\n\t\t}\n\t}\n\n\tallResults := append([]PanSearchItem(nil), firstPageResults...)\n\tpageCount := min((min(total, p.maxResults)+PageSize-1)/PageSize, MaxAPIPages)\n\tif pageCount > 1 {\n\t\ttype pageResult struct {\n\t\t\toffset int\n\t\t\titems  []PanSearchItem\n\t\t}\n\t\tresultCh := make(chan pageResult, pageCount-1)\n\t\tsem := make(chan struct{}, min(p.maxConcurrent, pageCount-1))\n\t\tvar wg sync.WaitGroup\n\n\t\tfor page := 1; page < pageCount; page++ {\n\t\t\toffset := page * PageSize\n\t\t\twg.Add(1)\n\t\t\tgo func() {","sourceCodeStart":510,"sourceCodeEnd":546,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/pansearch/pansearch.go#L510-L546","documentation":"When the first fetchFirstPage fails for any reason other than a 404, doSearch returns this wrapped error immediately without attempting a buildId refresh. It covers all non-404 failures of the initial search-API call.","triggerScenarios":"fetchFirstPage failed with a non-404 error: connection refused/reset, timeout, non-200 non-404 status (e.g. 500/503/429), or request-construction error.","commonSituations":"Upstream outage or maintenance (5xx), rate limiting (429), DNS/proxy misconfiguration, or the host being blocked by the upstream's anti-bot layer.","solutions":["Read the wrapped cause: 429/5xx → back off and retry later; connection errors → check network/proxy/DNS.","Reduce request frequency to avoid rate limiting.","Verify the upstream search endpoint is reachable from this host (curl the same URL).","A 404 would have triggered a buildId refresh automatically; for other statuses handle retry logic in the caller."],"exampleFix":"// before\nresults, err := plugin.Search(ctx, kw)\nif err != nil {\n    return err\n}\n// after\nresults, err := plugin.Search(ctx, kw)\nif err != nil {\n    if isRetryable(err) {\n        time.Sleep(3 * time.Second)\n        results, err = plugin.Search(ctx, kw)\n    }\n}","handlingStrategy":"retry","validationCode":"// probe upstream health before searching\nresp, err := http.Head(\"https://www.pansearch.me/\")\nif err != nil || resp.StatusCode >= 500 {\n    // skip search, upstream unavailable\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"获取首页失败\") {\n    if strings.Contains(err.Error(), \"context deadline exceeded\") {\n        // timeout: increase timeout and retry\n    } else if strings.Contains(err.Error(), \"429\") {\n        // rate limited: back off significantly\n    }\n}","preventionTips":["Classify the wrapped cause before choosing a retry strategy","Back off on 429/5xx instead of hammering the upstream","Check proxy/DNS config if connection errors persist"],"tags":["http","network","first-page","wrapped-error"],"backgroundTag":"api-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"}