{"record":{"id":"d5c0be9ede1bf167","repo":"fish2018/pansou","slug":"s-api-error-w","errorCode":null,"errorMessage":"%s API error: %w","messagePattern":"(.+?) API error: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"plugin/sousou/sousou.go","lineNumber":110,"sourceCode":"\t// 创建结果通道和错误通道\n\tresultChan := make(chan []SousouItem, len(supportedDiskTypes))\n\terrChan := make(chan error, len(supportedDiskTypes))\n\n\t// 创建等待组\n\tvar wg sync.WaitGroup\n\n\t// 并发搜索每种网盘类型\n\tfor _, diskType := range supportedDiskTypes {\n\t\twg.Add(1)\n\n\t\tgo func(dt string) {\n\t\t\tdefer wg.Done()\n\t\t\tdebugLog(\"开始搜索网盘类型: %s\", dt)\n\n\t\t\titems, err := p.searchByType(client, keyword, dt)\n\t\t\tif err != nil {\n\t\t\t\tdebugLog(\"%s 网盘搜索错误: %v\", dt, err)\n\t\t\t\terrChan <- fmt.Errorf(\"%s API error: %w\", dt, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tdebugLog(\"%s 网盘返回 %d 条结果\", dt, len(items))\n\t\t\tresultChan <- items\n\t\t}(diskType)\n\t}\n\n\t// 启动一个goroutine等待所有请求完成并关闭通道\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(resultChan)\n\t\tclose(errChan)\n\t}()\n\n\t// 收集结果\n\tvar allItems []SousouItem\n\tvar errors []error","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/sousou/sousou.go#L92-L128","documentation":"In the Sousou plugin, each disk type (e.g. 百度网盘, 阿里云盘) is searched concurrently in a goroutine. When searchByType fails for one type, the error is wrapped as '<diskType> API error: %w' and sent to errChan for aggregation; the overall search may still succeed using other types' results. This is a per-source-type failure record.","triggerScenarios":"searchByType(client, keyword, diskType) returns an error inside the per-disk-type goroutine spawned by the plugin's search fan-out; the wrapped error is pushed onto errChan.","commonSituations":"One specific pan provider changed its page structure or is rate-limiting, while other types still return results; keyword triggers a upstream 4xx.","solutions":["Check errChan entries to see which disk type failed and its underlying cause.","Retry only the failing disk type after backoff; treat partial success as acceptable.","Verify selectors/URL for that specific pan type — upstream layout may have changed.","Add per-type timeout and rate limiting to avoid hammering one provider."],"exampleFix":"// before\nerrChan <- fmt.Errorf(\"%s API error: %w\", dt, err)\n// after\ndebugLog(\"%s 网盘搜索错误: %v\", dt, err)\nerrChan <- fmt.Errorf(\"%s API error: %w\", dt, err) // collect; fail only if ALL types fail","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(keyword)\nif err != nil {\n    // aggregate: check whether ANY disk type succeeded; log per-type failures\n    // partial success from other types is often acceptable\n}","preventionTips":["Treat per-disk-type failures as partial, not fatal, when other types succeed","Apply per-type rate limits and timeouts","Alert on a specific disk type failing consistently (layout/endpoint change)","Keep per-type selectors configurable for quick fixes"],"tags":["concurrency","scraping","partial-failure","go"],"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"}