jackwener/OpenCLI · error · CommandExecutionError
xiaohongshu/unfollow: confirmation modal step failed (${conf
Error message
xiaohongshu/unfollow: confirmation modal step failed (${confirmResult.kind ?? 'no kind reported'}) What it means
The unfollow command's step 2 (clicking the confirm button in the unfollow confirmation modal) failed. The in-page script (buildConfirmModalScript) returned a result whose ok flag was false, and requireActionResult surfaced the modal's reported failure kind (e.g. modal not found, button not clickable). The library throws CommandExecutionError because without confirming the modal the unfollow action did not complete.
Source
Thrown at clis/xiaohongshu/unfollow.js:252
if (!clickResult.ok) {
throw new CommandExecutionError(
`xiaohongshu/unfollow failed: ${clickResult.reason ?? 'unknown reason'}`,
);
}
if (clickResult.state === 'not-following') {
return [{ status: 'not-following', user_id: userId, url }];
}
// Step 2: confirm the unfollow modal. Wait for the modal to mount
// first — xhs uses a CSS transition before the footer becomes
// interactive.
await page.wait({ time: MODAL_SETTLE_MS / 1000 });
const confirmResult = requireActionResult(
await page.evaluate(buildConfirmModalScript()),
'confirm-modal',
);
if (!confirmResult.ok) {
throw new CommandExecutionError(
`xiaohongshu/unfollow: confirmation modal step failed (${confirmResult.kind ?? 'no kind reported'})`,
);
}
// Step 3: verify the profile button text flipped back to 关注.
const verifyRaw = unwrapEvaluateResult(await page.evaluate(buildVerifyFollowFlippedScript()));
if (!verifyRaw || typeof verifyRaw !== 'object' || verifyRaw.ok !== true) {
throw new CommandExecutionError(
`xiaohongshu/unfollow: ${verifyRaw?.reason ?? 'state verification failed'}`,
);
}
return [{ status: 'unfollowed', user_id: userId, url }];
} catch (err) {
if (err instanceof CliError) throw err;
throw new CommandExecutionError(
`xiaohongshu/unfollow failed: ${err?.message ?? String(err)}`,
);
}View on GitHub (pinned to 49907e53dc)
Solutions
- Re-run the command once — slow hydration is often transient; the page.wait settle may just need more time
- Re-check the modal script selectors in buildConfirmModalScript against the live xiaohongshu.com DOM and update them after site redesigns
- Increase MODAL_SETTLE_MS or add an explicit wait-for-selector on the modal before evaluating
- Confirm the session is valid and the target user is actually followed (already-unfollowed targets may skip the modal)
Example fix
// before
await page.wait({ time: MODAL_SETTLE_MS / 1000 });
const confirmResult = requireActionResult(
await page.evaluate(buildConfirmModalScript()),
'confirm-modal',
);
// after
await page.waitForSelector('.confirm-modal, [class*=confirm]', { timeout: 10000 });
await page.wait({ time: MODAL_SETTLE_MS / 1000 });
const confirmResult = requireActionResult(
await page.evaluate(buildConfirmModalScript()),
'confirm-modal',
); Defensive patterns
Strategy: try-catch
Validate before calling
const modalReady = await page.evaluate(() => Boolean(document.querySelector('[class*=confirm], .modal')));
if (!modalReady) throw new Error('confirm modal not rendered yet'); Type guard
function isConfirmResult(r) { return r && typeof r === 'object' && r.ok === true; } Try / catch
try {
await unfollow(userId);
} catch (err) {
if (/confirmation modal step failed/.test(err.message)) {
await page.reload(); await retryWithBackoff(() => unfollow(userId));
} else throw err;
} Prevention
- Wait for the modal selector before evaluating instead of relying only on MODAL_SETTLE_MS
- Re-verify modal selectors after xiaohongshu.com frontend deploys
- Skip targets that are not currently followed to avoid no-modal cases
When it happens
Trigger: The confirm modal did not appear within MODAL_SETTLE_MS, the modal DOM structure changed (site update), the confirm button selector no longer matches, or page.evaluate returned null/undefined so requireActionResult wrapped it as a failed 'confirm-modal' action.
Common situations: Xiaohongshu ships a frontend redesign changing modal markup; slow page/network makes the modal render after the settle wait; an anti-bot overlay intercepts clicks; the user was already unfollowed so no modal appears.
Related errors
- Waiting for 12306 tk auth cookie
- ChatGPT composer is not available on the current page.
- Could not find the ChatGPT model selector in the composer.
- Could not click the ChatGPT ${target.label} model option.
- Could not find the ChatGPT tools menu button in the composer
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/262b5a2e80e5d162.
Report an issue: GitHub.