fish2018/pansou · error

解析响应失败

Error message

解析响应失败: %w

What it means

After fetching, fetchPage unmarshals the response body into Quark4KResponse; a JSON decode failure is wrapped as "解析响应失败". The body did not match the expected search-API JSON shape (data/included arrays), typically because the server returned an error page or a changed schema.

Solutions

  1. Log the first ~512 bytes of responseBody on failure to see whether it's HTML or unexpected JSON.
  2. Verify you got a 200 with real JSON (content-type check) before unmarshalling.
  3. Update Quark4KResponse struct fields/types to match the current API schema.
  4. Check the wrapped %w error — json.Unmarshal reports the exact offset/type mismatch.

Example fix

// before
var apiResp Quark4KResponse
if err := json.Unmarshal(responseBody, &apiResp); err != nil {
    return nil, false, fmt.Errorf("解析响应失败: %w", err)
}
// after
if !utf8.Valid(responseBody) || json.Valid(responseBody) == false {
    return nil, false, fmt.Errorf("解析响应失败: non-JSON body: %.200q", responseBody)
}
var apiResp Quark4KResponse
if err := json.Unmarshal(responseBody, &apiResp); err != nil {
    return nil, false, fmt.Errorf("解析响应失败: %w", err)
}
Defensive patterns

Strategy: type-guard

Validate before calling

if len(responseBody) == 0 || !json.Valid(responseBody) {
    return fmt.Errorf("response is not valid JSON (%d bytes)", len(responseBody))
}

Type guard

func isJSONBody(b []byte) bool {
    trimmed := bytes.TrimSpace(b)
    return len(trimmed) > 0 && trimmed[0] == '{' || (len(trimmed) > 0 && trimmed[0] == '[')
}

Try / catch

results, _, err := fetchPage(...)
if err != nil {
    if strings.Contains(err.Error(), "解析响应失败") {
        // dump body snippet, check for HTML/WAF page or schema drift
    }
}

Prevention

When it happens

Trigger: json.Unmarshal(responseBody, &apiResp) fails — body is HTML (WAF page), truncated, empty, or the API changed field types the struct cannot absorb (e.g. string where object expected).

Common situations: Cloudflare/anti-bot HTML page returned with 200 status; API version bump changing the response schema; proxy injecting an error page; keyword producing an unusual encoded response.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at plugin/quark4k/quark4k.go:242

		}
		
		// 状态码检查
		if resp.StatusCode != http.StatusOK {
			if i == p.retries {
				return nil, false, fmt.Errorf("API返回非200状态码: %d", resp.StatusCode)
			}
			time.Sleep(500 * time.Millisecond)
			continue
		}
		
		// 请求成功,跳出重试循环
		break
	}
	
	// 解析响应
	var apiResp Quark4KResponse
	if err := json.Unmarshal(responseBody, &apiResp); err != nil {
		return nil, false, fmt.Errorf("解析响应失败: %w", err)
	}
	
	// 处理结果
	results := make([]model.SearchResult, 0, len(apiResp.Data))
	
	// 从included数组中提取posts,创建帖子ID到帖子内容的映射
	postMap := make(map[string]Quark4KPost)
	for _, item := range apiResp.Included {
		// 只处理posts类型
		if item.Type == "posts" {
			// 将整个item转换为JSON字节,然后解析为Quark4KPost结构
			itemBytes, err := json.Marshal(item)
			if err == nil {
				var post Quark4KPost
				if err := json.Unmarshal(itemBytes, &post); err == nil {
					postMap[post.ID] = post
				}
			}

View on GitHub (pinned to beaa561337)