jackwener/OpenCLI · error · CommandExecutionError

Douyin search: unexpected evaluator payload shape

Error message

Douyin search: unexpected evaluator payload shape

What it means

The `douyin search` CLI extracts results by running WAIT_AND_EXTRACT_JS in the page and unwrapping the evaluator result. This CommandExecutionError fires when unwrapEvaluateResult returns a falsy value or a non-object, meaning the in-page evaluator never produced its expected {state, cards} payload. It signals a bridge/evaluation-layer failure rather than a Douyin page-state problem.

Source

Thrown at clis/douyin/search.js:279

        { name: 'query', required: true, positional: true, help: '搜索关键词' },
        { name: 'limit', type: 'int', default: 10, help: `结果数量 (1-${MAX_SEARCH_LIMIT})` },
    ],
    columns: ['rank', 'desc', 'author', 'url', 'plays', 'likes', 'comments', 'shares'],
    func: async (page, kwargs) => {
        const limit = parseSearchLimit(kwargs.limit);
        const keyword = String(kwargs.query ?? '').trim();
        if (!keyword) {
            throw new ArgumentError('douyin search 需要 <query> 关键词');
        }
        await page.goto(`https://www.douyin.com/search/${encodeURIComponent(keyword)}?type=video`);
        let result;
        try {
            result = unwrapEvaluateResult(await page.evaluate(WAIT_AND_EXTRACT_JS(RENDER_TIMEOUT_MS)));
        } catch (error) {
            throw new CommandExecutionError(`Douyin search extraction failed: ${error instanceof Error ? error.message : String(error)}`);
        }
        if (!result || typeof result !== 'object') {
            throw new CommandExecutionError('Douyin search: unexpected evaluator payload shape');
        }
        if (result.state === 'login_wall') {
            throw new AuthRequiredError(
                'www.douyin.com',
                'Douyin search results are blocked behind a login wall — log in at https://www.douyin.com in Chrome first.',
            );
        }
        if (result.state === 'empty') {
            throw new EmptyResultError('douyin search', `No Douyin videos matched "${keyword}".`);
        }
        if (result.state === 'timeout') {
            throw new CommandExecutionError('Douyin search did not render result cards within the timeout. Open the same search in Chrome and verify login/security state before retrying.');
        }
        if (!Array.isArray(result.cards)) {
            throw new CommandExecutionError('Douyin search: evaluator returned malformed cards payload');
        }
        if (result.cards.length === 0) {
            throw new EmptyResultError('douyin search', `No Douyin videos matched "${keyword}".`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command — transient bridge/hydration failures often resolve on a second attempt.
  2. Verify the bound Chrome instance and opencli browser bridge are the versions the CLI expects (restart Chrome with the bridge extension reloaded).
  3. Confirm the page is www.douyin.com/search/... and did not navigate or crash during the 15s render window.
  4. Check unwrapEvaluateResult against the current WAIT_AND_EXTRACT_JS return shape; update the adapter if the evaluator contract changed.

Example fix

// adapter-side hardening
// before
const result = unwrapEvaluateResult(await page.evaluate(WAIT_AND_EXTRACT_JS(RENDER_TIMEOUT_MS)));
// after
const raw = await page.evaluate(WAIT_AND_EXTRACT_JS(RENDER_TIMEOUT_MS));
const result = unwrapEvaluateResult(raw) ?? null;
if (!result || typeof result !== 'object' || typeof result.state !== 'string') {
    throw new CommandExecutionError(`Douyin search: bad evaluator payload: ${JSON.stringify(raw)?.slice(0, 200)}`);
}
Defensive patterns

Strategy: type-guard

Validate before calling

const raw = await page.evaluate(WAIT_AND_EXTRACT_JS(RENDER_TIMEOUT_MS));
if (raw == null) throw new Error('evaluator returned nothing');

Type guard

function isEvaluatorPayload(v) {
  return v != null && typeof v === 'object' && !Array.isArray(v) && typeof v.state === 'string';
}

Try / catch

try {
  const result = unwrapEvaluateResult(await page.evaluate(WAIT_AND_EXTRACT_JS(RENDER_TIMEOUT_MS)));
  if (!isEvaluatorPayload(result)) throw new CommandExecutionError('Douyin search: unexpected evaluator payload shape');
} catch (e) {
  if (e instanceof CommandExecutionError) { logPayloadDebug(); /* retry once */ }
  else throw e;
}

Prevention

When it happens

Trigger: page.evaluate() resolves to null/undefined (e.g. the evaluator script returned nothing, the value failed to serialize across the CDP/bridge boundary), or unwrapEvaluateResult strips a wrapper and yields a primitive like a string or number instead of the expected state object.

Common situations: Browser bridge/extension version mismatch after an opencli upgrade; the page navigated away mid-evaluate; browser context hung and returned undefined; serialization of the payload failed because of exotic values returned by the in-page script.

Related errors


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