jackwener/OpenCLI · error · CommandExecutionError

xiaohongshu/unfollow: expected profile ${userId}, got ${pars

Error message

xiaohongshu/unfollow: expected profile ${userId}, got ${parsedHref.pathname}

What it means

After reaching the profile page, the command re-checks that the URL still identifies the requested user's profile (/user/profile/<same id>). If the final pathname does not match the requested userId (redirect to another profile, home page, 404, or a modified URL), this CommandExecutionError reporting the expected ID and actual pathname is thrown.

Source

Thrown at clis/xiaohongshu/unfollow.js:224

            await page.goto(url);
            await page.wait({ time: PROFILE_SETTLE_MS / 1000 });

            const hrefRaw = unwrapEvaluateResult(await page.evaluate('() => location.href'));
            if (typeof hrefRaw !== 'string') {
                throw new CommandExecutionError('xiaohongshu/unfollow: malformed current-url payload');
            }
            const parsedHref = new URL(hrefRaw);
            if (parsedHref.protocol !== 'https:' || !isXiaohongshuHost(parsedHref.hostname)) {
                throw new CommandExecutionError(
                    `xiaohongshu/unfollow: 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/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 }];
            }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the user ID is correct and the profile loads in a normal browser (no redirect) before re-running
  2. Increase tolerance for slow loads: retry the command so the SPA finishes redirecting before the check
  3. Complete any captcha/risk-control challenge shown in the automated browser, then retry
  4. If the account no longer exists, treat the target as gone instead of retrying

Example fix

// before
$ xiaohongshu unfollow 000000000000000000000000  # deleted user, redirects to /
// after: confirm the profile URL opens in a browser first
$ xiaohongshu unfollow 5d8f88dc0000000001005d3a
Defensive patterns

Strategy: validation

Validate before calling

// verify the target profile exists and does not redirect before automating
const res = await fetch(`https://www.xiaohongshu.com/user/profile/${id}`, { redirect: 'manual', headers: { 'user-agent': 'Mozilla/5.0' } });
if (res.status >= 300 && res.status < 400) throw new Error('Profile redirects — ID may be invalid/deleted');

Type guard

function expectProfilePath(href, userId) { try { return new URL(href).pathname.match(/^\/user\/profile\/([a-zA-Z0-9]{8,32})\/?$/)?.[1] === userId; } catch { return false; } }

Try / catch

try { await cli.unfollow({ 'user-id': id }); } catch (e) { if (/expected profile /.test(String(e.message))) { /* validate the ID / confirm profile exists, then retry once */ } else throw e; }

Prevention

When it happens

Trigger: The profile ID does not exist or was deleted, so xiaohongshu redirects (e.g. to home or an error page); the account is banned/private and the site bounces to another route; the ID passed had casing/characters that got normalized differently; slow SPA redirect after settle time; a bot-detection redirect to a generic route.

Common situations: Unfollowing a user who deleted/deactivated their account; typo or stale ID from an old database; page still mid-redirect when location.href was sampled (settle time too short on a slow network); risk-control redirect after too many automated actions.

Related errors


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