{"record":{"id":"8e78d511fde8b7fb","repo":"fish2018/pansou","slug":"s-api-s","errorCode":null,"errorMessage":"[%s] API错误: %s","messagePattern":"\\[(.+?)\\] API错误: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/ikantv/ikantv.go","lineNumber":103,"sourceCode":"\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\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 读取响应失败: %w\", p.Name(), err)\n\t}\n\n\tvar apiResp apiResponse\n\tif err := json.Unmarshal(body, &apiResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] JSON解析失败: %w\", p.Name(), err)\n\t}\n\tif apiResp.Code != 0 {\n\t\treturn nil, fmt.Errorf(\"[%s] API错误: %s\", p.Name(), apiResp.Message)\n\t}\n\n\tresults := convertResults(apiResp.Data)\n\treturn plugin.FilterResultsByKeyword(results, keyword), nil\n}\n\nfunc convertResults(items []apiItem) []model.SearchResult {\n\tresults := make([]model.SearchResult, 0, len(items))\n\tfor _, item := range items {\n\t\tresult, ok := convertResult(item)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tresults = append(results, result)\n\t}\n\treturn results\n}\n","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/ikantv/ikantv.go#L85-L121","documentation":"ikantv plugin's doSearch throws this when the decoded apiResponse.Code is not 0 — the API returned an application-level error and its Message field is surfaced verbatim. HTTP transport succeeded; the API rejected the request or reported an internal problem.","triggerScenarios":"apiResp.Code != 0 after successful parse: invalid/missing auth parameter, rate limit at the application layer, unsupported query, or upstream service degraded with an error code in the JSON envelope.","commonSituations":"The ikantv API changed its status-code convention (success no longer 0); the query triggered a server-side rejection; upstream partial outage reports business errors in the JSON body.","solutions":["Read apiResp.Message in the error — it states the API's own reason.","Verify the plugin's assumption that Code==0 means success against the current API contract and update if the convention changed.","Check whether the request needs updated auth/params; retry if the message suggests a transient upstream issue.","Add handling for known error codes (rate limit, auth) instead of surfacing a generic message."],"exampleFix":"// before\nif apiResp.Code != 0 {\n\treturn nil, fmt.Errorf(\"[%s] API错误: %s\", p.Name(), apiResp.Message)\n}\n// after\nif apiResp.Code != 0 {\n\tif isRetryable(apiResp.Code) {\n\t\treturn nil, fmt.Errorf(\"[%s] API错误(可重试): code=%d %s\", p.Name(), apiResp.Code, apiResp.Message)\n\t}\n\treturn nil, fmt.Errorf(\"[%s] API错误: code=%d %s\", p.Name(), apiResp.Code, apiResp.Message)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"results, err := p.doSearch(ctx, keyword)\nif err != nil && strings.Contains(err.Error(), \"API错误\") {\n\t// application-level rejection: read the embedded apiResp.Message,\n\t// decide retry vs fix-params vs give up\n\tlog.Printf(\"ikantv API rejected request: %v\", err)\n}","preventionTips":["Surface apiResp.Message to the user/diagnostics — it explains the rejection","Confirm the success-code convention (Code==0) still matches the current API","Do not retry blindly; distinguish quota/auth errors from transient ones","Keep a per-source error taxonomy so business errors degrade that source only"],"tags":["api","go","upstream","business-logic"],"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"}