{"record":{"id":"b8529549ae38942c","repo":"fish2018/pansou","slug":"api-returned-error-s","errorCode":null,"errorMessage":"API returned error: %s","messagePattern":"API returned error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/jikepan/jikepan.go","lineNumber":102,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"request failed: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\t\n\t// 解析响应\n\tvar apiResp JikepanResponse\n\tbodyBytes, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read response body failed: %w\", err)\n\t}\n\t\n\tif err := json.Unmarshal(bodyBytes, &apiResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode response failed: %w\", err)\n\t}\n\t\n\t// 检查响应状态\n\tif apiResp.Msg != \"success\" {\n\t\treturn nil, fmt.Errorf(\"API returned error: %s\", apiResp.Msg)\n\t}\n\t\n\t// 转换结果格式\n\tresults := p.convertResults(apiResp.List)\n\t\n\treturn results, nil\n}\n\n// convertResults 将API响应转换为标准SearchResult格式\nfunc (p *JikepanAsyncV2Plugin) convertResults(items []JikepanItem) []model.SearchResult {\n\tresults := make([]model.SearchResult, 0, len(items))\n\t\n\tfor i, item := range items {\n\t\t// 跳过没有链接的结果\n\t\tif len(item.Links) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\t","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/jikepan/jikepan.go#L84-L120","documentation":"API-level error in jikepan's doSearch (plugin/jikepan/jikepan.go:102): the JSON decoded correctly but the response's Msg field is not \"success\", so the API itself reported a failure; the actual message is carried in the error text.","triggerScenarios":"The Jikepan API responds with valid JSON whose msg field is not exactly \"success\" — e.g. quota exhausted, invalid token/params, or empty query handled server-side.","commonSituations":"API 端点升级后返回新的错误码；请求频率触发配额限制。","solutions":["Log apiResp.Msg and apiResp.Code to see the exact upstream reason","Compare against a set of accepted success values or check a code field instead of a hardcoded string","Verify the request body matches the current API contract (keyword, is_all, pagination)","Check jikepan.xyz service status / account quota"],"exampleFix":"// before\nif apiResp.Msg != \"success\" {\n\treturn nil, fmt.Errorf(\"API returned error: %s\", apiResp.Msg)\n}\n\n// after\nif apiResp.Code != 0 && apiResp.Msg != \"success\" {\n\treturn nil, fmt.Errorf(\"API returned error (code=%d): %s\", apiResp.Code, apiResp.Msg)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Go\nresults, err := p.doSearch(ctx, keyword, ext)\nif err != nil {\n\tif strings.HasPrefix(err.Error(), \"API returned error:\") {\n\t\tlog.Printf(\"jikepan business error: %v\", err)\n\t\treturn nil, plugin.ErrUpstreamRejected\n\t}\n\treturn nil, err\n}","preventionTips":["Log msg (and any code field) from every response","Treat non-success msg as upstream, not local, failure","Track API contract changes","Handle quota/auth-style messages distinctly"],"tags":["api","upstream","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"}