jackwener/OpenCLI · error · CommandExecutionError

Xiaohongshu search masonry remained collapsed after one fres

Error message

Xiaohongshu search masonry remained collapsed after one fresh-tab recovery.

What it means

Thrown when a Xiaohongshu search returned a collapsed masonry render, the library already attempted its one built-in recovery (replacing the tab with a fresh one and re-collecting), and the results were still collapsed. It means the site served a degraded/anti-bot or not-logged-in render even after recovery, so no usable masonry could be obtained.

Source

Thrown at clis/xiaohongshu/search.js:895

        { name: 'note-type', type: 'string', default: 'all', choices: ['all', 'video', 'image'], help: 'Note type' },
        { name: 'publish-time', type: 'string', default: 'anytime', choices: ['anytime', 'day', 'week', 'half-year'], help: 'Publish time range' },
        { name: 'scope', type: 'string', default: 'all', choices: ['all', 'seen', 'unseen', 'following'], help: 'Search scope' },
        { name: 'location', type: 'string', default: 'all', choices: ['all', 'same-city', 'nearby'], help: 'Location distance' },
    ],
    columns: ['rank', 'title', 'author', 'likes', 'published_at', 'url'],
    func: async (page, kwargs) => {
        try {
            const limit = parseLimit(kwargs.limit);
            const requestedFilters = resolveSearchFilters(kwargs);
            const keyword = encodeURIComponent(kwargs.query);
            const url = `https://www.xiaohongshu.com/search_result?keyword=${keyword}&source=web_search_result_notes`;
            await page.goto(url);
            let harvest = await collectSearchHarvest(page, limit, requestedFilters);
            if (isCollapsedRender(harvest.diag)) {
                await replaceCollapsedTab(page, url);
                harvest = await collectSearchHarvest(page, limit, requestedFilters);
                if (isCollapsedRender(harvest.diag)) {
                    throw new CommandExecutionError(
                        'Xiaohongshu search masonry remained collapsed after one fresh-tab recovery.',
                        'Retry later or use a different logged-in browser session.',
                    );
                }
            }
            const rows = harvest.rows
                .filter((item) => item.title)
                .slice(0, limit);
            if (rows.length === 0) {
                throw new EmptyResultError('xiaohongshu search', 'No usable notes were rendered for this query.');
            }
            return rows
                .map((item, i) => ({
                rank: i + 1,
                ...item,
                published_at: noteIdToDate(item.url),
            }));
        }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Wait and retry later — the collapsed render is usually a temporary rate-limit/risk-control response.
  2. Log in again or point the CLI at a different logged-in browser session (as the hint says).
  3. Reduce query frequency / add delays between searches to avoid triggering risk control.
  4. Verify the browser profile actually has an active xiaohongshu.com login by opening the site manually.
  5. Update the library in case Xiaohongshu changed the collapsed-render markup that isCollapsedRender detects.

Example fix

// before
await runSearch(query); // throws on collapsed render
// after
await sleep(backoffMs);
try {
  await runSearch(query);
} catch (e) {
  if (String(e).includes('remained collapsed')) await switchToFreshSessionAndRetry(query);
  else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

// before searching, confirm the session can render a known page's masonry
const probe = await collectSearchHarvest(probePage, 1, {});
if (isCollapsedRender(probe.diag)) throw new Error('session currently serving collapsed renders; wait before searching');

Try / catch

try {
  await xhsSearch(query);
} catch (err) {
  if (String(err.message).includes('remained collapsed')) {
    await sleep(60_000);
    return retryWithFreshSession(query);
  }
  throw err;
}

Prevention

When it happens

Trigger: collectSearchHarvest() returns diag where isCollapsedRender(harvest.diag) is true, replaceCollapsedTab(page, url) succeeds, the harvest is re-run, and isCollapsedRender(harvest.diag) is still true.

Common situations: Xiaohongshu rate-limiting or risk control serving the collapsed view to the session; expired login cookies in the browser profile; scraping too frequently from the same session; site-side A/B changes to the collapsed page.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/ed35a9929c3ae9cf. Report an issue: GitHub.