xpzouying/xiaohongshu-mcp · error
failed to unmarshal feeds: %w
Error message
failed to unmarshal feeds: %w
What it means
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.
Source
Thrown at xiaohongshu/search.go:162
if (window.__INITIAL_STATE__ &&
window.__INITIAL_STATE__.search &&
window.__INITIAL_STATE__.search.feeds) {
const feeds = window.__INITIAL_STATE__.search.feeds;
const feedsData = feeds.value !== undefined ? feeds.value : feeds._value;
if (feedsData) {
return JSON.stringify(feedsData);
}
}
return "";
}`).String()
if result == "" {
return nil, errors.ErrNoFeeds
}
var feeds []Feed
if err := json.Unmarshal([]byte(result), &feeds); err != nil {
return nil, fmt.Errorf("failed to unmarshal feeds: %w", err)
}
return onlyNotes(feeds), nil
}
// feedIDsJS 读当前结果集的 id 列表,用来判断数据有没有换一批。
const feedIDsJS = `() => {
const f = window.__INITIAL_STATE__?.search?.feeds;
const v = f ? (f.value !== undefined ? f.value : f._value) : null;
return v ? v.map(x => x.id).join(",") : "";
}`
func readFeedIDs(page *rod.Page) string {
res, err := page.Eval(feedIDsJS)
if err != nil {
return ""
}
return res.Value.Str()View on GitHub (pinned to 332d196854)
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
Example fix
// before
var feeds []Feed
if err := json.Unmarshal([]byte(result), &feeds); err != nil { ... }
// after
type Feed struct {
ID flexibleString `json:"id"` // accepts string or number
...
}
type flexibleString string
func (f *flexibleString) UnmarshalJSON(b []byte) error {
b = bytes.Trim(b, "\"")
*f = flexibleString(b); return nil
} Defensive patterns
Strategy: try-catch
Validate before calling
// sanity-check the payload shape before strict unmarshal
var probe []map[string]any
if err := json.Unmarshal([]byte(result), &probe); err != nil {
return fmt.Errorf("feeds payload is not a JSON array of objects: %w", err)
}
if len(probe) > 0 {
if _, ok := probe[0]["id"]; !ok {
log.Printf("warning: feeds items lack 'id' field — schema drift suspected")
}
} Try / catch
feeds, err := action.Search(ctx, keyword, filters...)
if err != nil && strings.Contains(err.Error(), "failed to unmarshal feeds") {
log.Printf("XHS feeds schema drift: %v", err)
// degrade gracefully: report empty result + alert instead of failing the job
return nil, fmt.Errorf("search results unreadable, library likely outdated: %w", err)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- unmarshal noteDetailMap failed
- failed to unmarshal noteDetailMap: %w
- failed to unmarshal feeds: %w
- 解析通知列表失败: %w
- unmarshal current user failed
AI-assisted analysis of xpzouying/xiaohongshu-mcp@332d196854 (2026-09-05).
Data as JSON: /api/errors/3609ba416b2b93a9.
Report an issue: GitHub.