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

  1. Dump the raw result JSON (log it before Unmarshal) and diff against the Feed struct; update struct tags to match the new schema
  2. Add json.RawMessage / flexible types (e.g. custom UnmarshalJSON for id that accepts string|number) for fields XHS toggles
  3. Upgrade the library/package version if a newer release already tracks the schema change
  4. 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

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


AI-assisted analysis of xpzouying/xiaohongshu-mcp@332d196854 (2026-09-05). Data as JSON: /api/errors/3609ba416b2b93a9. Report an issue: GitHub.