fish2018/pansou · error
解析响应失败
Error message
解析响应失败: %w
What it means
After a successful request, fetchPage unmarshals the response body into BixinResponse with json.Unmarshal; this error wraps any parse failure. It means the body is not valid JSON or its shape does not match BixinResponse (e.g. an HTML error page or a different field type). Unlike transport errors, no retry is attempted here.
Solutions
- Log a snippet of responseBody to see whether it's HTML, empty, or JSON with a different schema.
- Add a content-type check (must be application/json) before unmarshalling.
- Update the BixinResponse struct tags to match the current API schema.
- Use json.Unmarshal with a generic map first to inspect the actual shape when debugging.
Example fix
// before
var apiResp BixinResponse
if err := json.Unmarshal(responseBody, &apiResp); err != nil {
return nil, false, fmt.Errorf("解析响应失败: %w", err)
}
// after
if !json.Valid(responseBody) {
return nil, false, fmt.Errorf("响应不是有效JSON: %q", responseBody[:min(len(responseBody),200)])
}
var apiResp BixinResponse
if err := json.Unmarshal(responseBody, &apiResp); err != nil {
return nil, false, fmt.Errorf("解析响应失败: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
body, _ := io.ReadAll(resp.Body)
if !json.Valid(body) {
return fmt.Errorf("响应不是有效JSON")
}
if ct := resp.Header.Get("Content-Type"); !strings.Contains(ct, "application/json") {
return fmt.Errorf("意外的Content-Type: %s", ct)
} Type guard
func isValidBixinJSON(body []byte) bool {
var probe struct {
Data []json.RawMessage `json:"data"`
}
return json.Unmarshal(body, &probe) == nil
} Try / catch
results, err := plugin.Search(ctx, keyword)
if err != nil {
var parseErr *json.SyntaxError
if errors.As(err, &parseErr) {
log.Printf("bixin返回了非JSON内容(偏移%d): %v", parseErr.Offset, parseErr)
}
} Prevention
- Validate Content-Type is application/json before unmarshalling
- Keep struct tags in sync with the live API schema (add regression tests with fixture JSON)
- Log raw response snippets when parsing fails
- Handle HTML challenge pages explicitly instead of letting them reach the parser
When it happens
Trigger: json.Unmarshal(responseBody, &apiResp) fails in fetchPage — response body is HTML (anti-bot page), empty, truncated, or JSON fields have types that don't match BixinResponse's struct tags.
Common situations: Site returns an HTML login/challenge page instead of JSON, API version changed its response schema, or a non-JSON error body slipped through because only status code was checked.
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/53f3c62c437f8f1b.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/bixin/bixin.go:241
}
// 状态码检查
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 BixinResponse
if err := json.Unmarshal(responseBody, &apiResp); err != nil {
return nil, false, fmt.Errorf("解析响应失败: %w", err)
}
// 处理结果
results := make([]model.SearchResult, 0, len(apiResp.Data))
postMap := make(map[string]BixinPost)
// 创建帖子ID到帖子内容的映射
for _, post := range apiResp.Included {
postMap[post.ID] = post
}
// 遍历搜索结果
for _, discussion := range apiResp.Data {
// 获取相关帖子
postID := discussion.Relationships.MostRelevantPost.Data.ID
post, ok := postMap[postID]
if !ok {
continueView on GitHub (pinned to beaa561337)