{"record":{"id":"38c6557765ea5a9b","repo":"fish2018/pansou","slug":"s-api-s-38c655","errorCode":null,"errorMessage":"[%s] API返回错误: %s","messagePattern":"\\[(.+?)\\] API返回错误: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/wanou/wanou.go","lineNumber":159,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\t\n\t// 解析JSON响应\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 读取响应失败: %w\", p.Name(), err)\n\t}\n\t\n\tvar apiResponse WanouAPIResponse\n\tif err := json.Unmarshal(body, &apiResponse); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 解析JSON响应失败: %w\", p.Name(), err)\n\t}\n\t\n\t// 检查API响应状态\n\tif apiResponse.Code != 1 {\n\t\treturn nil, fmt.Errorf(\"[%s] API返回错误: %s\", p.Name(), apiResponse.Msg)\n\t}\n\t\n\t// 解析搜索结果\n\tvar results []model.SearchResult\n\tfor _, item := range apiResponse.List {\n\t\tif result := p.parseAPIItem(item); result.Title != \"\" {\n\t\t\tresults = append(results, result)\n\t\t}\n\t}\n\t\n\treturn results, nil\n}\n\n// WanouAPIResponse API响应结构\ntype WanouAPIResponse struct {\n\tCode      int           `json:\"code\"`\n\tMsg       string        `json:\"msg\"`\n\tPage      int           `json:\"page\"`","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/wanou/wanou.go#L141-L177","documentation":"The wanou API wraps results in an envelope with a code field; code==1 means success. If the API returns any other code, searchImpl surfaces 'API返回错误' together with the API's own message (apiResponse.Msg). This is an application-level error from the upstream API, not a transport error.","triggerScenarios":"Calling SearchWithResult when the upstream responds with valid JSON but code != 1 — e.g. invalid/missing token, quota exhausted, banned account, deprecated API version, or empty/invalid keyword.","commonSituations":"API key or session expired; free-tier rate limit reached; the aggregator changed its API contract (old code semantics); keyword contains banned terms; server-side maintenance mode.","solutions":["Log apiResponse.Msg — it usually states the real cause (token/quota/etc.)","Refresh any token/credentials the API requires","Update the client to the current API version and code semantics","Add special handling (e.g. backoff) if the code indicates rate limiting"],"exampleFix":"// before\nif apiResponse.Code != 1 {\n    return nil, fmt.Errorf(\"[%s] API返回错误: %s\", p.Name(), apiResponse.Msg)\n}\n// after\nif apiResponse.Code != 1 {\n    if apiResponse.Code == codeRateLimited {\n        time.Sleep(backoff)\n        return p.searchImpl(query) // retry once\n    }\n    return nil, fmt.Errorf(\"[%s] API返回错误(code=%d): %s\", p.Name(), apiResponse.Code, apiResponse.Msg)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(ctx, query)\nvar apiErr *APIError\nif errors.As(err, &apiErr) {\n    switch apiErr.Code {\n    case codeAuth: refreshTokenAndRetry()\n    case codeQuota: backoffAndRetryLater()\n    default: log.Errorf(\"upstream API error: %s\", apiErr.Msg)\n    }\n}","preventionTips":["Log the API's Msg field — it names the real cause","Refresh tokens/credentials proactively","Track upstream API contract changes","Respect documented rate limits and quotas"],"tags":["api","upstream","error-response"],"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"}