fish2018/pansou · error
解析结果 JSON 失败
Error message
解析结果 JSON 失败: %w
What it means
Returned by parseEmbeddedList when jsonutil.Unmarshal fails to decode the extracted, escape-decoded string into []searchItem. The embedded string was found and scanned correctly, but its contents are not a JSON array matching the searchItem schema. Typically this means the site changed the data shape (array to object, added wrapper, renamed structure) or the JS-escape decoding produced corrupt bytes.
Solutions
- errors.Unwrap to see the json syntax/type error and byte offset, then fetch and inspect the decoded payload at that offset
- Update the searchItem struct's json tags/types to match the new upstream schema
- If the root became an object, adjust parseEmbeddedList to unwrap the results field before unmarshal
- Verify decodeJSSingleQuotedString output is valid UTF-8 (utf8.Valid) before unmarshaling
Example fix
// before
type searchItem struct {
ID int64 `json:"id"`
URL string `json:"url"`
}
// after — tolerate upstream sending id as string
var items []struct {
ID json.Number `json:"id"`
URL string `json:"url"`
} Defensive patterns
Strategy: try-catch
Validate before calling
decoded, err := decodeJSSingleQuotedString(encoded)
if err != nil { return err }
if !utf8.Valid(decoded) {
return fmt.Errorf("decoded payload is not valid UTF-8")
} Type guard
func looksLikeItemArray(b []byte) bool {
var probe []map[string]json.RawMessage
return json.Unmarshal(b, &probe) == nil
} Try / catch
var jsonErr *json.UnmarshalTypeError
if errors.As(err, &jsonErr) {
// schema drift at field jsonErr.Field: update searchItem tags
} else if errors.As(err, &syntaxErr) {
// decoding bug or truncated payload
} Prevention
- Pin searchItem schema against recorded upstream payloads in unit tests
- Validate decoded bytes are UTF-8 before unmarshal
- Log the offending byte offset from the json error for diagnosis
- Wrap searchItem field types loosely (json.Number) to survive upstream type drift
When it happens
Trigger: The inline 'const list = JSON.parse('...' )' string unquotes to JSON that does not fit []searchItem: root is an object not an array, fields have different types (e.g. id became a string), or decodeJSSingleQuotedString mangled multi-byte content.
Common situations: Upstream schema evolution (is_type values change, url field renamed); decoder bug with unusual \x or surrogate-pair escapes; page contains a marker-like string in a comment with non-JSON content following it.
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/99536007aa16aa5c.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/haitunsou/haitunsou.go:162
start = index + len(marker)
break
}
}
if start < 0 {
return nil, fmt.Errorf("未找到页面内嵌结果")
}
encoded, err := scanSingleQuotedString(body, start)
if err != nil {
return nil, err
}
decoded, err := decodeJSSingleQuotedString(encoded)
if err != nil {
return nil, err
}
var items []searchItem
if err := jsonutil.Unmarshal(decoded, &items); err != nil {
return nil, fmt.Errorf("解析结果 JSON 失败: %w", err)
}
return items, nil
}
func indexBytes(data, marker []byte) int {
return bytes.Index(data, marker)
}
func scanSingleQuotedString(body []byte, start int) ([]byte, error) {
escaped := false
for index := start; index < len(body); index++ {
if escaped {
escaped = false
continue
}
switch body[index] {
case '\\':
escaped = trueView on GitHub (pinned to beaa561337)