{"record":{"id":"ab81f8326c047fc9","repo":"fish2018/pansou","slug":"s-s-d-api-s","errorCode":null,"errorMessage":"[%s] %s网盘第%d页API错误: %s","messagePattern":"\\[(.+?)\\] (.+?)网盘第(.+?)页API错误: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/haisou/haisou.go","lineNumber":364,"sourceCode":"\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页返回状态码: %d\", p.Name(), panType, pageNo, resp.StatusCode)\n\t}\n\n\t// 读取响应体\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页读取响应失败: %w\", p.Name(), panType, pageNo, err)\n\t}\n\n\t// 解析响应\n\tvar apiResp SearchAPIResponse\n\tif err := json.Unmarshal(body, &apiResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页JSON解析失败: %w\", p.Name(), panType, pageNo, err)\n\t}\n\n\t// 检查API响应状态\n\tif apiResp.Code != 0 {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页API错误: %s\", p.Name(), panType, pageNo, apiResp.Msg)\n\t}\n\n\tif DebugLog {\n\t\tfmt.Printf(\"[%s] %s网盘第%d页获取到 %d 个搜索结果\\n\", p.Name(), panType, pageNo, len(apiResp.Data.List))\n\t}\n\n\treturn apiResp.Data.List, nil\n}\n\n// fetchShareLink 通过hsid获取具体的分享链接\nfunc (p *HaisouPlugin) fetchShareLink(client *http.Client, hsid string, platform string) (string, string, error) {\n\t// 构建获取链接的URL\n\tfetchURL := fmt.Sprintf(\"https://haisou.cc/api/pan/share/%s/fetch\", hsid)\n\n\tif DebugLog {\n\t\tfmt.Printf(\"[%s] 获取链接 hsid=%s platform=%s: %s\\n\", p.Name(), hsid, platform, fetchURL)\n\t}\n","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/haisou/haisou.go#L346-L382","documentation":"This error is thrown when the haisou search API returned parseable JSON but the business status field apiResp.Code is not 0, meaning the upstream API rejected the search request for that pan type and page. The API's error message (apiResp.Msg) is embedded so the developer can see the upstream reason (e.g. invalid key, quota exceeded, bad parameters).","triggerScenarios":"fetchSearchPage receives a JSON response whose Code field is non-zero, after successful unmarshalling of the search API response.","commonSituations":"Upstream API key/token expired; search keyword blocked or too short; page number beyond available results; upstream rate limiting reported via Code; API changed its status-code semantics.","solutions":["Inspect the embedded apiResp.Msg for the upstream reason and address it accordingly","Verify API credentials/parameters sent in the search request","Retry with a different pageNo or reduced query frequency to rule out rate limiting","Check upstream API documentation/changelog for new status codes","Add handling for well-known Code values (e.g. auto-refresh token, back off on quota)."],"exampleFix":"// before\nif apiResp.Code != 0 {\n\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页API错误: %s\", p.Name(), panType, pageNo, apiResp.Msg)\n}\n// after\nif apiResp.Code != 0 {\n\tif isRetryableCode(apiResp.Code) {\n\t\ttime.Sleep(retryBackoff(pageNo))\n\t\treturn p.fetchSearchPage(panType, keyword, pageNo)\n\t}\n\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页API错误: code=%d msg=%s\", p.Name(), panType, pageNo, apiResp.Code, apiResp.Msg)\n}","handlingStrategy":"fallback","validationCode":"// no pre-call validation possible; Code is only known after the response\n// defend by handling non-zero codes in a switch on known values","typeGuard":"func isUpstreamOK(resp SearchAPIResponse) bool { return resp.Code == 0 }","tryCatchPattern":"var apiResp SearchAPIResponse\nif err := json.Unmarshal(body, &apiResp); err != nil { return err }\nif apiResp.Code != 0 {\n\treturn fmt.Errorf(\"upstream rejected search: code=%d msg=%s\", apiResp.Code, apiResp.Msg)\n}","preventionTips":["Monitor apiResp.Code/Msg distribution in logs to catch upstream changes early","Keep upstream API credentials/tokens valid and rotated","Throttle request rate to avoid upstream-side rejections","Skip the failing pan type and continue with others instead of aborting the whole search"],"tags":["api","upstream","error-response","go"],"backgroundTag":"api-error-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"}