jackwener/OpenCLI · error · CommandExecutionError

Xiaohongshu search filter layout did not match the expected

Error message

Xiaohongshu search filter layout did not match the expected visible panel (${detail}).

What it means

This CommandExecutionError is the fall-through branch of requireFilterApplication: the in-page filter script returned a status the handler does not recognize (anything other than ok/auth/timeout/location/capability/inactive), and the message describes it as the filter layout not matching the expected visible panel. It guards against the filter panel's DOM diverging from the selectors the script relies on (e.g. the panel count differing from 1 or an unrecognized result shape).

Source

Thrown at clis/xiaohongshu/search.js:521

        return;
    }
    const detail = typeof result.detail === 'string' ? result.detail : 'unknown';
    if (result.status === 'auth') {
        throw new AuthRequiredError('www.xiaohongshu.com', 'Xiaohongshu search filters require a logged-in browser session');
    }
    if (result.status === 'timeout') {
        throw new TimeoutError(`xiaohongshu search filter ${detail}`, FILTER_SETTLE_SECONDS);
    }
    if (result.status === 'location') {
        throw new CommandExecutionError(`Xiaohongshu location filter was not applied (${detail}); enable browser geolocation permission.`);
    }
    if (result.status === 'capability') {
        throw new CommandExecutionError(`Xiaohongshu account-scoped filter was unavailable (${detail}); verify login and account access.`);
    }
    if (result.status === 'inactive') {
        throw new CommandExecutionError(`Xiaohongshu search filter chip did not become active (${detail}).`);
    }
    throw new CommandExecutionError(`Xiaohongshu search filter layout did not match the expected visible panel (${detail}).`);
}
/**
 * Build a "scroll until enough or plateaued" IIFE used in place of a fixed
 * `autoScroll({ times: N })`. Xiaohongshu's search results page lazy-loads
 * ~5-7 notes per scroll, so the previous `times: 2` capped extraction at
 * ~13 items regardless of `--limit` (see #1471). This helper drives scrolls
 * dynamically:
 *
 *   - count visible `section.note-item` rows (excluding related-search
 *     `.query-note-item` rows)
 *   - if count >= targetCount → break (got enough)
 *   - if two consecutive scrolls add no new rows → break (DOM plateaued,
 *     no more lazy-load available)
 *   - hard cap at `maxScrolls` iterations (default 15) to bound runtime
 *
 * Exported so the rednote adapter (same DOM shape) can reuse it.
 */
export function buildScrollUntilJs(targetCount, maxScrolls = 15) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Upgrade the CLI to the newest version where selectors are updated for the current xiaohongshu.com layout.
  2. Retry the command — A/B layouts sometimes resolve to the standard one on a fresh session.
  3. Run without filters as a workaround, applying results filtering client-side.
  4. Disable browser extensions (ad blockers, privacy tools) that may strip panel DOM nodes.
  5. Inspect the page in the automation browser and file an issue with the current panel markup.

Example fix

// before
await cli.search({ query: 'coffee', filters: [{ group: 'sort', option: 'time' }] });
// after
// fallback when panel layout changed
try {
  await cli.search({ query: 'coffee', filters: [{ group: 'sort', option: 'time' }] });
} catch (e) {
  if (/filter layout did not match/.test(e.message)) {
    const raw = await cli.search({ query: 'coffee' }); // unfiltered fallback
    // filter results manually
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the panel exists before applying filters
const panelVisible = await page.locator('section.filter, [class*=filter-panel]').first().isVisible();
if (!panelVisible) throw new Error('filter panel not rendered; skip filters');

Type guard

function isFilterResult(r) {
  return r !== null && typeof r === 'object' && !Array.isArray(r)
    && typeof r.status === 'string'
    && ['ok','auth','timeout','location','capability','inactive'].includes(r.status);
}

Try / catch

try {
  await xhs.search({ query, filters });
} catch (e) {
  if (e instanceof CommandExecutionError && /layout did not match/.test(e.message)) {
    const results = await xhs.search({ query }); // unfiltered fallback
    return clientSideFilter(results, filters);
  }
  throw e;
}

Prevention

When it happens

Trigger: Running the search with filters when the filter panel structure changed on xiaohongshu.com so openPanel() returns non-ok statuses not explicitly mapped, or the in-page script returns an unexpected payload shape such as a non-object or missing status.

Common situations: Xiaohongshu ships a UI redesign (new panel markup, renamed classes); an older CLI version scraping a newer page layout; A/B-tested layouts served to some sessions; overly aggressive ad-blocking extensions removing panel nodes.

Related errors


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