fish2018/pansou · error
api returned error
Error message
api returned error: %s
What it means
After successful decoding, searchPage checks the application-level code in MelostResponse; if apiResp.Code != 200 it returns "api returned error: %s" with the API's Msg field. This is a business-logic error from the melost.cn API itself: the HTTP layer succeeded but the API refused the search (invalid params, quota, auth ticket, or captcha required).
Solutions
- Read apiResp.Msg in the error text — the API states the exact reason (quota, ticket, captcha)
- If Msg indicates captcha/ticket, populate adv_params.search_code or obtain a fresh search_ticket before searching
- Reduce request frequency/concurrency if the code indicates rate limiting
- Re-check the request parameter contract against the current melost.cn frontend to find rejected fields
- Since doSearch only surfaces this when all pages fail, verify whether the code applies to specific pages only
Example fix
// before: message only
if apiResp.Code != 200 {
return nil, fmt.Errorf("api returned error: %s", apiResp.Msg)
}
// after: include code for programmatic handling
if apiResp.Code != 200 {
return nil, fmt.Errorf("api returned error: code=%d msg=%s", apiResp.Code, apiResp.Msg)
} Defensive patterns
Strategy: try-catch
Validate before calling
// validate inputs the API is known to reject before calling
if strings.TrimSpace(keyword) == "" {
return nil, fmt.Errorf("keyword required")
} Try / catch
if apiResp.Code != 200 {
switch {
case strings.Contains(apiResp.Msg, "ticket") || strings.Contains(apiResp.Msg, "验证"):
// refresh search_ticket / handle captcha, then retry
case strings.Contains(apiResp.Msg, "频繁") || strings.Contains(apiResp.Msg, "limit"):
// back off; rate limited
default:
return nil, fmt.Errorf("api returned error: %s", apiResp.Msg)
}
} Prevention
- Always log the API's Msg and code for actionable diagnosis
- Maintain a fresh search_ticket/search_code flow if the API requires one
- Rate-limit and back off based on the API's quota messages
- Track schema/message changes from melost.cn to catch new rejection reasons
When it happens
Trigger: melost.cn responds 200 with an envelope whose code is not 200 (e.g. rate-limit code, missing/invalid search_ticket, required captcha/search_code, or query rejected), and Msg carries the API's reason.
Common situations: Search quota exhausted for the client/IP, the API now requires a valid search_ticket or search_code (anti-bot escalation), invalid parameter combination rejected by the backend, or empty/over-filtered queries flagged server-side.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/29e1abdc9f3e92ee.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/melost/melost.go:191
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read response failed: %w", err)
}
var apiResp MelostResponse
if err := json.Unmarshal(respBody, &apiResp); err != nil {
return nil, fmt.Errorf("decode response failed: %w", err)
}
if apiResp.Code != 200 {
return nil, fmt.Errorf("api returned error: %s", apiResp.Msg)
}
return apiResp.Data.List, nil
}
func (p *MelostAsyncPlugin) deduplicateItems(items []MelostItem) []MelostItem {
uniqueMap := make(map[string]MelostItem)
for _, item := range items {
key := item.DiskID
if key == "" {
key = item.Link
}
if key == "" {
key = item.DiskName + "|" + item.DiskType
}
existing, exists := uniqueMap[key]View on GitHub (pinned to beaa561337)