jackwener/OpenCLI · error · CommandExecutionError
xiaohongshu/follow failed: ${result.reason ?? 'unknown reaso
Error message
xiaohongshu/follow failed: ${result.reason ?? 'unknown reason'} What it means
Thrown when the in-page follow script runs but reports failure via result.ok === false. requireActionResult guarantees the payload shape, so reaching this throw means the DOM automation itself failed (button not found, click did not flip state, etc.) and the script returned a reason. The command surfaces result.reason, or 'unknown reason' when absent.
Source
Thrown at clis/xiaohongshu/follow.js:203
`xiaohongshu/follow: expected Xiaohongshu profile host, got ${parsedHref.hostname}`,
);
}
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/follow: expected profile ${userId}, got ${parsedHref.pathname}`,
);
}
const result = requireActionResult(
await page.evaluate(buildFollowScript()),
'follow-action',
);
if (!result.ok) {
throw new CommandExecutionError(
`xiaohongshu/follow failed: ${result.reason ?? 'unknown reason'}`,
);
}
return [{ status: result.state, user_id: userId, url }];
} catch (err) {
if (err instanceof CliError) throw err;
throw new CommandExecutionError(
`xiaohongshu/follow failed: ${err?.message ?? String(err)}`,
);
}
},
});
export const __test__ = {
assertUserId,
buildFollowScript,
};
View on GitHub (pinned to 49907e53dc)
Solutions
- Read result.reason in the message and address the specific failure (e.g. button-not-found vs timeout).
- Retry — transient rendering delays commonly cause the state-flip timeout.
- Manually confirm the follow action works in the logged-in browser; re-login if the session is degraded.
- Update the CLI if XHS changed its profile-header DOM.
Example fix
// before
await cli.run(['xiaohongshu', 'follow', userId]); // flaky first attempt
// after
try {
await cli.run(['xiaohongshu', 'follow', userId]);
} catch (e) {
if (/timeout|button/i.test(e.message)) await cli.run(['xiaohongshu', 'follow', userId]); // one retry
else throw e;
} Defensive patterns
Strategy: retry
Validate before calling
// Confirm session is live before automating
const probe = await page.evaluate(() => !!document.querySelector('[role=button], button'));
if (!probe) throw new Error('page not ready for follow action'); Type guard
function isActionResult(v) {
return !!v && typeof v === 'object' && !Array.isArray(v) && typeof v.ok === 'boolean';
} Try / catch
try {
await follow(userId);
} catch (err) {
if (/follow failed/.test(err.message)) {
await sleep(2000);
await follow(userId); // single retry for transient DOM/timing issues
} else throw err;
} Prevention
- Allow the profile page to fully settle before automation runs.
- Retry once on failure — state-flip timeouts are often transient.
- Keep the CLI updated for XHS DOM changes.
- Verify the account isn't action-limited by following manually first.
When it happens
Trigger: page.evaluate(buildFollowScript()) returns { ok: false, reason } — the follow button was not located in the profile header, the button text never flipped to 已关注 within the 5s STATE_FLIP_TIMEOUT_MS, or the click was intercepted.
Common situations: User already blocked/restricted interactions; XHS DOM changed so selectors no longer match; page not fully settled when the script ran; network slowness exceeding the 5s poll window.
Related errors
- Failed to fill rename input.
- Codex picker did not open after typing the command.
- Failed to follow: HTTP ' + r2.status
- Failed to open Instagram reel composer
- Instagram reel upload input not found
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/cfb3f5b7f3850091.
Report an issue: GitHub.