{"record":{"id":"dd92d1def528f3ad","repo":"fish2018/pansou","slug":"s-d-dd92d1","errorCode":null,"errorMessage":"[%s] 搜索返回状态码: %d","messagePattern":"\\[(.+?)\\] 搜索返回状态码: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/alupan/alupan.go","lineNumber":138,"sourceCode":"\tsearchURL := fmt.Sprintf(\"https://www.aliupan.com/?s=%s\", url.QueryEscape(keyword))\n\tctx, cancel := context.WithTimeout(context.Background(), searchTimeout)\n\tdefer cancel()\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 创建请求失败: %w\", p.Name(), err)\n\t}\n\n\tsetCommonHeaders(req, \"https://www.aliupan.com/\")\n\n\tresp, err := p.doRequestWithRetry(req, client, searchMaxRetries)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\tdoc, err := goquery.NewDocumentFromReader(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 解析搜索页面失败: %w\", p.Name(), err)\n\t}\n\n\tvar (\n\t\tresults []model.SearchResult\n\t\twg      sync.WaitGroup\n\t\tmu      sync.Mutex\n\t\tsem     = make(chan struct{}, maxConcurrency)\n\t)\n\n\tdoc.Find(\"article.excerpt\").Each(func(_ int, item *goquery.Selection) {\n\t\ttitleSel := item.Find(\"header h2 a\")\n\t\ttitle := strings.TrimSpace(titleSel.Text())\n\t\tdetailURL, ok := titleSel.Attr(\"href\")","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/alupan/alupan.go#L120-L156","documentation":"searchImpl rejects any alupan search response whose HTTP status code is not 200. The site responded, but with an error/redirect status (e.g. 403 anti-bot block, 429 rate limit, 5xx server error, or 3xx redirect not followed), so the HTML parsing step would be meaningless.","triggerScenarios":"doRequestWithRetry returns a response whose StatusCode != http.StatusOK — typically 403/429 (bot protection, rate limiting), 404 (site structure changed), 500/502/503 (server-side problems), or 301/302 when redirects are disabled.","commonSituations":"The site enabled Cloudflare or WAF bot detection and returns 403 to the plugin's headers; the host is rate-limited after heavy scraping (429); the site is temporarily down (5xx); a URL or redirect policy change in the http.Client stops redirects from being followed.","solutions":["Log resp.Status and the response body snippet to see what the site actually returned (403 pages often contain a WAF challenge).","Compare headers sent by setCommonHeaders with a real browser request; update User-Agent/Referer/Accept to avoid bot detection.","Honor 429: add backoff (retryBaseDelay already exists) and reduce request concurrency (maxConcurrency = 12 may be too aggressive).","Handle 3xx explicitly by checking client.CheckRedirect if redirects are being stopped.","Treat 5xx as transient — surface it to the retry loop instead of failing immediately."],"exampleFix":"// before\nif resp.StatusCode != http.StatusOK {\n    return nil, fmt.Errorf(\"[%s] 搜索返回状态码: %d\", p.Name(), resp.StatusCode)\n}\n// after\nif resp.StatusCode == http.StatusTooManyRequests {\n    time.Sleep(retryBaseDelay * 2)\n    // retry or return a typed rate-limit error\n}\nif resp.StatusCode != http.StatusOK {\n    body, _ := io.ReadAll(io.LimitReader(resp.Body, 512))\n    return nil, fmt.Errorf(\"[%s] 搜索返回状态码: %d, body: %s\", p.Name(), resp.StatusCode, body)\n}","handlingStrategy":"fallback","validationCode":"// After receiving a response, inspect status before parsing\nif resp.StatusCode != http.StatusOK {\n    body, _ := io.ReadAll(io.LimitReader(resp.Body, 256))\n    log.Printf(\"aliupan status=%d body=%q\", resp.StatusCode, body)\n}","typeGuard":null,"tryCatchPattern":"// Go: treat non-200 as a typed condition and fall back\nresults, err := plugin.Search(keyword, ext)\nif err != nil && strings.Contains(err.Error(), \"搜索返回状态码\") {\n    // extract code, apply backoff for 429/5xx, then continue with other plugins\n    time.Sleep(retryBaseDelay)\n}","preventionTips":["Match real browser headers (User-Agent, Accept, Referer) to avoid 403 WAF blocks.","Throttle concurrency and honor 429 Retry-After to avoid rate limiting.","Retry 5xx as transient; fail fast on 4xx after logging the body.","Alert on status-code changes — they usually mean the site changed its protection or structure."],"tags":["http","status-code","anti-bot","rate-limit","go"],"backgroundTag":"http-non-200-response","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"}