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
- 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
- Re-login / clear any captcha or restriction, then retry
- If the site UI changed, update SCOPE_SELECTORS/FOLLOWING_LABELS in buildClickUnfollowScript to match the new markup
- 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
- Verify the profile renders the 已关注 button in the automated browser before running
- Re-login and clear captchas/rate limits before batch unfollows
- Throttle automation to avoid risk-control walls
- After site UI changes, update SCOPE_SELECTORS and button labels in the injected scripts
- Never run against your own profile (no follow button)
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
- ChatGPT composer is not available on the current page.
- Could not find the ChatGPT model selector in the composer.
- Could not find the ChatGPT tools menu button in the composer
- Instagram reel caption editor did not appear
- SignOut svg not found
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/04ce9cb5224fd0e6.
Report an issue: GitHub.