fish2018/pansou · error

api returned error

Error message

api returned error: %s

What it means

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.

Solutions

  1. Check the Msg string in the error for the API's stated reason
  2. Verify the request params (wd keyword, page, limit) match the API's current expectations
  3. Refresh any tokens/cookies the yunso API now requires
  4. Skip or retry per API code: transient codes retry, auth codes require re-auth

Example fix

// before
if apiResp.Code != 0 {
    return nil, fmt.Errorf("api returned error: %s", apiResp.Msg)
}
// after
if apiResp.Code != 0 {
    if isTransientCode(apiResp.Code) {
        return nil, retryableError{fmt.Errorf("api returned error: %s", apiResp.Msg)}
    }
    return nil, fmt.Errorf("api returned error: code=%d msg=%s", apiResp.Code, apiResp.Msg)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if strings.TrimSpace(keyword) == "" {
    return errors.New("keyword required for yunso search")
}

Try / catch

items, err := searchPage(ctx, client, keyword, page)
if err != nil && strings.Contains(err.Error(), "api returned error") {
    log.Warn("yunso api rejected query", "msg", err)
    return emptyResult // don't retry application-level refusals blindly
}

Prevention

When it happens

Trigger: 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.

Common situations: Searching banned or empty keywords, the API requiring an updated token/cookie after a site change, or the service degrading during peak load.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/91a646cf49d3874d. Report an issue: GitHub.

Appendix: source

Thrown at plugin/yunso/yunso.go:190

	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("read response failed: %w", err)
	}

	var apiResp YunsoAPIResponse
	if err := jsonutil.Unmarshal(body, &apiResp); err != nil {
		return nil, fmt.Errorf("decode response failed: %w", err)
	}

	if apiResp.Code != 0 {
		return nil, fmt.Errorf("api returned error: %s", apiResp.Msg)
	}

	return p.parseItems(apiResp.Data)
}

func (p *YunsoAsyncPlugin) parseItems(fragment string) ([]YunsoItem, error) {
	doc, err := goquery.NewDocumentFromReader(strings.NewReader(`<div id="yunso-root">` + fragment + `</div>`))
	if err != nil {
		return nil, fmt.Errorf("parse html failed: %w", err)
	}

	items := make([]YunsoItem, 0, 16)
	doc.Find("div.layui-card[data-qid]").Each(func(_ int, card *goquery.Selection) {
		anchor := card.Find(`a[onclick*="open_sid"]`).First()
		if anchor.Length() == 0 {
			return
		}

View on GitHub (pinned to beaa561337)