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
- Log the first ~512 bytes of responseBody on failure to see whether it's HTML or unexpected JSON.
- Verify you got a 200 with real JSON (content-type check) before unmarshalling.
- Update Quark4KResponse struct fields/types to match the current API schema.
- 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
- Check Content-Type is application/json before unmarshalling
- Keep the Quark4KResponse struct in sync with the live API schema
- Treat any HTML in a 200 response as an anti-bot signal, not a parse bug
- Use json.Valid as a cheap pre-check before full unmarshal
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)