jackwener/OpenCLI · error · CommandExecutionError

xiaohongshu/unfollow failed: ${clickResult.reason ?? 'unknow

Error message

xiaohongshu/unfollow failed: ${clickResult.reason ?? 'unknown reason'}

What it means

The in-page unfollow script returns {ok:false, state:'failed', reason} when it cannot find the follow-state button (已关注/已互关) on the profile. The CLI wraps that reason into this CommandExecutionError. Per the script, typical reasons include being logged out, being blocked/limited, or the site's DOM/class names changing so the button selectors no longer match.

Source

Thrown at clis/xiaohongshu/unfollow.js:235

                );
            }
            if (/\/login(?:[/?#]|$)/i.test(parsedHref.pathname)) {
                throw new AuthRequiredError('www.xiaohongshu.com');
            }
            const currentProfile = parsedHref.pathname.match(/^\/user\/profile\/([a-zA-Z0-9]{8,32})\/?$/);
            if (currentProfile?.[1] !== userId) {
                throw new CommandExecutionError(
                    `xiaohongshu/unfollow: expected profile ${userId}, got ${parsedHref.pathname}`,
                );
            }

            // Step 1: click 已关注 (idempotent — bails out if 关注 is visible)
            const clickResult = requireActionResult(
                await page.evaluate(buildClickUnfollowScript()),
                'click-unfollow',
            );
            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'})`,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read the reason in the message: 'Follow-state button not found' usually means logged out/blocked or selectors changed — open the profile manually in the automated Chrome and confirm the 已关注 button is visible
  2. Re-login / clear any captcha or restriction, then retry
  3. If the site UI changed, update SCOPE_SELECTORS/FOLLOWING_LABELS in buildClickUnfollowScript to match the new markup
  4. Increase the settle wait or retry to let the profile fully render on slow networks; ensure you are not targeting your own profile

Example fix

// before: settle too short, header not rendered
await page.wait({ time: 2.5 });
// after: allow longer settle / retry before evaluating
await page.wait({ time: 5 });
// or retry the command once before assuming UI change
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check the follow button exists on the page before invoking the command (in browser context)
const btn = [...document.querySelectorAll('button, [role="button"]')].find(b => ['已关注','已互关','互相关注','关注'].includes((b.innerText||'').trim()));
if (!btn) console.warn('Follow-state button not rendered yet — wait or expect failure');

Type guard

function isActionResult(v) { return !!v && typeof v === 'object' && typeof v.ok === 'boolean'; }

Try / catch

try { await cli.unfollow({ 'user-id': id }); } catch (e) { if (/unfollow failed:/.test(String(e.message))) { /* check reason: re-login if logged out, or update selectors if the site UI changed */ } else throw e; }

Prevention

When it happens

Trigger: The profile page rendered without the follow-state button (logged-out view, restricted account, soft-ban/captcha overlay); xiaohongshu shipped a UI redesign changing button markup so SCOPE_SELECTORS/button-text matching fails; page content not settled when the script ran; viewing a profile where the button is hidden (e.g. self profile or blocked user).

Common situations: Long-running automation broken by a site UI update; heavy rate-limiting showing a verification wall instead of the profile; running on your own profile (no 已关注 button); network slow so the profile header hadn't rendered within PROFILE_SETTLE_MS.

Related errors


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