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
- Re-run the command — transient bridge/hydration failures often resolve on a second attempt.
- Verify the bound Chrome instance and opencli browser bridge are the versions the CLI expects (restart Chrome with the bridge extension reloaded).
- Confirm the page is www.douyin.com/search/... and did not navigate or crash during the 15s render window.
- 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
- Keep the opencli browser bridge/extension and CLI on the same version.
- Assert the evaluator payload shape in a unit test against a recorded fixture.
- Log the raw evaluate() result when unwrapping fails to speed diagnosis.
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
- ${label}: ${String(payload.error)}
- ${label} returned malformed extraction payload
- coupang add-to-cart evaluation failed: ${error?.message || e
- Douyin API request failed (${method} ${url}): ${error instan
- ${context}: malformed evaluate payload
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/dbc5f0f311674b5f.
Report an issue: GitHub.