{"record":{"id":"3609ba416b2b93a9","repo":"xpzouying/xiaohongshu-mcp","slug":"failed-to-unmarshal-feeds-w-3609ba","errorCode":null,"errorMessage":"failed to unmarshal feeds: %w","messagePattern":"failed to unmarshal feeds: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"xiaohongshu/search.go","lineNumber":162,"sourceCode":"\t\tif (window.__INITIAL_STATE__ &&\n\t\t    window.__INITIAL_STATE__.search &&\n\t\t    window.__INITIAL_STATE__.search.feeds) {\n\t\t\tconst feeds = window.__INITIAL_STATE__.search.feeds;\n\t\t\tconst feedsData = feeds.value !== undefined ? feeds.value : feeds._value;\n\t\t\tif (feedsData) {\n\t\t\t\treturn JSON.stringify(feedsData);\n\t\t\t}\n\t\t}\n\t\treturn \"\";\n\t}`).String()\n\n\tif result == \"\" {\n\t\treturn nil, errors.ErrNoFeeds\n\t}\n\n\tvar feeds []Feed\n\tif err := json.Unmarshal([]byte(result), &feeds); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to unmarshal feeds: %w\", err)\n\t}\n\n\treturn onlyNotes(feeds), nil\n}\n\n// feedIDsJS 读当前结果集的 id 列表，用来判断数据有没有换一批。\nconst feedIDsJS = `() => {\n\tconst f = window.__INITIAL_STATE__?.search?.feeds;\n\tconst v = f ? (f.value !== undefined ? f.value : f._value) : null;\n\treturn v ? v.map(x => x.id).join(\",\") : \"\";\n}`\n\nfunc readFeedIDs(page *rod.Page) string {\n\tres, err := page.Eval(feedIDsJS)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn res.Value.Str()","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/xiaohongshu/search.go#L144-L180","documentation":"Search() reads the final results from window.__INITIAL_STATE__.search.feeds via page.MustEval and JSON-stringifies them in the browser. That JSON is unmarshalled into []Feed in Go; if the browser-side payload does not match the Feed struct (schema drift), unmarshalling fails and the raw json error is wrapped with this prefix.","triggerScenarios":"Search completes and returns non-empty feeds JSON, but XHS changed the feeds schema (renamed/retyped fields, e.g. id/type/card fields) so it no longer fits []Feed; or nested structures changed shape.","commonSituations":"XHS frontend update changing __INITIAL_STATE__ field names or types (string id vs number); new required-ish fields with different types breaking strict decoding; loading an outdated package version against the current XHS site.","solutions":["Dump the raw result JSON (log it before Unmarshal) and diff against the Feed struct; update struct tags to match the new schema","Add json.RawMessage / flexible types (e.g. custom UnmarshalJSON for id that accepts string|number) for fields XHS toggles","Upgrade the library/package version if a newer release already tracks the schema change","Unmarshal into map[string]any first as a diagnostic step when the error occurs in production"],"exampleFix":"// before\nvar feeds []Feed\nif err := json.Unmarshal([]byte(result), &feeds); err != nil { ... }\n// after\ntype Feed struct {\n    ID flexibleString `json:\"id\"` // accepts string or number\n    ...\n}\ntype flexibleString string\nfunc (f *flexibleString) UnmarshalJSON(b []byte) error {\n    b = bytes.Trim(b, \"\\\"\")\n    *f = flexibleString(b); return nil\n}","handlingStrategy":"try-catch","validationCode":"// sanity-check the payload shape before strict unmarshal\nvar probe []map[string]any\nif err := json.Unmarshal([]byte(result), &probe); err != nil {\n    return fmt.Errorf(\"feeds payload is not a JSON array of objects: %w\", err)\n}\nif len(probe) > 0 {\n    if _, ok := probe[0][\"id\"]; !ok {\n        log.Printf(\"warning: feeds items lack 'id' field — schema drift suspected\")\n    }\n}","typeGuard":null,"tryCatchPattern":"feeds, err := action.Search(ctx, keyword, filters...)\nif err != nil && strings.Contains(err.Error(), \"failed to unmarshal feeds\") {\n    log.Printf(\"XHS feeds schema drift: %v\", err)\n    // degrade gracefully: report empty result + alert instead of failing the job\n    return nil, fmt.Errorf(\"search results unreadable, library likely outdated: %w\", err)\n}","preventionTips":["Log the raw feeds JSON on unmarshal failure for offline diagnosis","Use tolerant types (custom UnmarshalJSON for id-like fields that may be string or number)","Keep the package updated against XHS frontend changes","Write a nightly smoke test that runs one Search and asserts unmarshal succeeds"],"tags":["json","go","schema-mismatch","encoding"],"backgroundTag":"json-unmarshal-schema-mismatch","analyzedSha":"332d196854a9eac0d2b8c2c0e3d0cc43139d724c","analyzedAt":"2026-09-05T22:22:55.988Z","contentChangedAt":"2026-09-05T22:22:55.988Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}