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

  1. Re-run the command once — slow hydration is often transient; the page.wait settle may just need more time
  2. Re-check the modal script selectors in buildConfirmModalScript against the live xiaohongshu.com DOM and update them after site redesigns
  3. Increase MODAL_SETTLE_MS or add an explicit wait-for-selector on the modal before evaluating
  4. 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

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


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