fish2018/pansou · error
响应缺少 merged_by_type
Error message
响应缺少 merged_by_type
What it means
The Seroval response parsed successfully, but navigating node.result -> searchResults -> merged_by_type (with reference resolution) yielded a nil node or a node without Props. The plugin requires merged_by_type to enumerate search results grouped by link type, so it returns this error when the expected structure is missing.
Solutions
- Verify the server function ID is current by re-running discovery; an invalid ID commonly yields an unexpected payload shape.
- Confirm with a browser DevTools capture what a real (non-empty) response looks like and compare node paths (result.searchResults.merged_by_type).
- Handle the empty-results case explicitly before erroring if zero hits legitimately omit merged_by_type.
- Update the decoder navigation keys if the field was renamed upstream.
Example fix
// before
if mergedNode == nil || mergedNode.Props == nil {
return nil, fmt.Errorf("响应缺少 merged_by_type")
}
// after
if mergedNode == nil || mergedNode.Props == nil {
return []model.SearchResult{}, nil // treat as zero results
} Defensive patterns
Strategy: type-guard
Validate before calling
var probe map[string]stdjson.RawMessage
if stdjson.Unmarshal(body, &probe) == nil {
if _, ok := probe["result"]; !ok {
return fmt.Errorf("response lacks result envelope")
}
} Type guard
func hasMergedByType(root *serovalNode, d *serovalDecoder) bool {
n := d.resolve(d.objectValue(d.objectValue(d.objectValue(root, "result"), "searchResults"), "merged_by_type"))
return n != nil && n.Props != nil
} Try / catch
results, err := parseSearchResponse(body)
if err != nil {
if strings.Contains(err.Error(), "响应缺少 merged_by_type") {
return []model.SearchResult{}, nil // treat as empty result set
}
return nil, err
} Prevention
- Treat a missing merged_by_type with zero-result keywords as an empty set, not a hard error.
- Capture a real browser response periodically to detect schema drift early.
- Re-run function-ID discovery when payload shapes look wrong.
When it happens
Trigger: decoder.objectValue/resolve produce a nil merged_by_type node or one with nil Props: the API call succeeded (valid JSON) but returned no search data — zero results shape, an API-level error payload, or a schema change.
Common situations: The search keyword returned no results and the API omits merged_by_type; the server function ID is wrong so a different payload shape comes back; the site upgraded and renamed/renested result fields.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/e69b6806ebca20e3.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/xiaokupan/xiaokupan.go:274
hashes := hashPattern.FindAll(assetBody[windowStart:routeIndex], -1)
if len(hashes) == 0 {
return "", fmt.Errorf("入口脚本未找到搜索接口标识")
}
return string(hashes[len(hashes)-1]), nil
}
func parseSearchResponse(body []byte) ([]model.SearchResult, error) {
var root serovalNode
if err := stdjson.Unmarshal(body, &root); err != nil {
return nil, fmt.Errorf("解析 Seroval 响应失败: %w", err)
}
decoder := newSerovalDecoder(&root)
resultNode := decoder.objectValue(&root, "result")
searchResultsNode := decoder.objectValue(resultNode, "searchResults")
mergedNode := decoder.objectValue(searchResultsNode, "merged_by_type")
mergedNode = decoder.resolve(mergedNode)
if mergedNode == nil || mergedNode.Props == nil {
return nil, fmt.Errorf("响应缺少 merged_by_type")
}
results := make([]model.SearchResult, 0)
seenURLs := make(map[string]struct{})
for index, linkType := range mergedNode.Props.Keys {
if index >= len(mergedNode.Props.Values) {
break
}
arrayNode := decoder.resolve(mergedNode.Props.Values[index])
if arrayNode == nil || arrayNode.Type != 9 {
continue
}
for _, itemNode := range arrayNode.Array {
resourceURL := strings.TrimSpace(decoder.stringValue(decoder.objectValue(itemNode, "url")))
if !validResourceURL(resourceURL, linkType) {
continue
}
if _, exists := seenURLs[resourceURL]; exists {View on GitHub (pinned to beaa561337)