{"record":{"id":"91a646cf49d3874d","repo":"fish2018/pansou","slug":"api-returned-error-s-91a646","errorCode":null,"errorMessage":"api returned error: %s","messagePattern":"api returned error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/yunso/yunso.go","lineNumber":190,"sourceCode":"\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n\t}\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read response failed: %w\", err)\n\t}\n\n\tvar apiResp YunsoAPIResponse\n\tif err := jsonutil.Unmarshal(body, &apiResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode response failed: %w\", err)\n\t}\n\n\tif apiResp.Code != 0 {\n\t\treturn nil, fmt.Errorf(\"api returned error: %s\", apiResp.Msg)\n\t}\n\n\treturn p.parseItems(apiResp.Data)\n}\n\nfunc (p *YunsoAsyncPlugin) parseItems(fragment string) ([]YunsoItem, error) {\n\tdoc, err := goquery.NewDocumentFromReader(strings.NewReader(`<div id=\"yunso-root\">` + fragment + `</div>`))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse html failed: %w\", err)\n\t}\n\n\titems := make([]YunsoItem, 0, 16)\n\tdoc.Find(\"div.layui-card[data-qid]\").Each(func(_ int, card *goquery.Selection) {\n\t\tanchor := card.Find(`a[onclick*=\"open_sid\"]`).First()\n\t\tif anchor.Length() == 0 {\n\t\t\treturn\n\t\t}\n","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/yunso/yunso.go#L172-L208","documentation":"The yunso API returned a valid JSON envelope but with a non-zero Code field, indicating an application-level error. The plugin surfaces the API's Msg string. This is the upstream service rejecting the query or reporting an internal problem, not a transport/parse failure.","triggerScenarios":"searchPage decodes YunsoAPIResponse with Code != 0 — e.g. invalid/empty keyword, missing login token required by the API, keyword censored/blocked, or upstream internal error reported in Msg.","commonSituations":"Searching banned or empty keywords, the API requiring an updated token/cookie after a site change, or the service degrading during peak load.","solutions":["Check the Msg string in the error for the API's stated reason","Verify the request params (wd keyword, page, limit) match the API's current expectations","Refresh any tokens/cookies the yunso API now requires","Skip or retry per API code: transient codes retry, auth codes require re-auth"],"exampleFix":"// before\nif apiResp.Code != 0 {\n    return nil, fmt.Errorf(\"api returned error: %s\", apiResp.Msg)\n}\n// after\nif apiResp.Code != 0 {\n    if isTransientCode(apiResp.Code) {\n        return nil, retryableError{fmt.Errorf(\"api returned error: %s\", apiResp.Msg)}\n    }\n    return nil, fmt.Errorf(\"api returned error: code=%d msg=%s\", apiResp.Code, apiResp.Msg)\n}","handlingStrategy":"try-catch","validationCode":"if strings.TrimSpace(keyword) == \"\" {\n    return errors.New(\"keyword required for yunso search\")\n}","typeGuard":null,"tryCatchPattern":"items, err := searchPage(ctx, client, keyword, page)\nif err != nil && strings.Contains(err.Error(), \"api returned error\") {\n    log.Warn(\"yunso api rejected query\", \"msg\", err)\n    return emptyResult // don't retry application-level refusals blindly\n}","preventionTips":["Sanitize/validate keywords before querying","Classify API error codes: retry transient, skip permanent","Refresh tokens/cookies when auth-style errors appear","Watch Msg strings for upstream contract changes"],"tags":["api","upstream","scraping"],"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"}