{"record":{"id":"a0529aa30a3210e2","repo":"jackwener/OpenCLI","slug":"xiaohongshu-unfollow-expected-profile-userid","errorCode":null,"errorMessage":"xiaohongshu/unfollow: expected profile ${userId}, got ${parsedHref.pathname}","messagePattern":"xiaohongshu/unfollow: expected profile (.+?), got (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/unfollow.js","lineNumber":224,"sourceCode":"            await page.goto(url);\n            await page.wait({ time: PROFILE_SETTLE_MS / 1000 });\n\n            const hrefRaw = unwrapEvaluateResult(await page.evaluate('() => location.href'));\n            if (typeof hrefRaw !== 'string') {\n                throw new CommandExecutionError('xiaohongshu/unfollow: malformed current-url payload');\n            }\n            const parsedHref = new URL(hrefRaw);\n            if (parsedHref.protocol !== 'https:' || !isXiaohongshuHost(parsedHref.hostname)) {\n                throw new CommandExecutionError(\n                    `xiaohongshu/unfollow: expected Xiaohongshu profile host, got ${parsedHref.hostname}`,\n                );\n            }\n            if (/\\/login(?:[/?#]|$)/i.test(parsedHref.pathname)) {\n                throw new AuthRequiredError('www.xiaohongshu.com');\n            }\n            const currentProfile = parsedHref.pathname.match(/^\\/user\\/profile\\/([a-zA-Z0-9]{8,32})\\/?$/);\n            if (currentProfile?.[1] !== userId) {\n                throw new CommandExecutionError(\n                    `xiaohongshu/unfollow: expected profile ${userId}, got ${parsedHref.pathname}`,\n                );\n            }\n\n            // Step 1: click 已关注 (idempotent — bails out if 关注 is visible)\n            const clickResult = requireActionResult(\n                await page.evaluate(buildClickUnfollowScript()),\n                'click-unfollow',\n            );\n            if (!clickResult.ok) {\n                throw new CommandExecutionError(\n                    `xiaohongshu/unfollow failed: ${clickResult.reason ?? 'unknown reason'}`,\n                );\n            }\n            if (clickResult.state === 'not-following') {\n                return [{ status: 'not-following', user_id: userId, url }];\n            }\n","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/unfollow.js#L206-L242","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the user ID is correct and the profile loads in a normal browser (no redirect) before re-running","Increase tolerance for slow loads: retry the command so the SPA finishes redirecting before the check","Complete any captcha/risk-control challenge shown in the automated browser, then retry","If the account no longer exists, treat the target as gone instead of retrying"],"exampleFix":"// before\n$ xiaohongshu unfollow 000000000000000000000000  # deleted user, redirects to /\n// after: confirm the profile URL opens in a browser first\n$ xiaohongshu unfollow 5d8f88dc0000000001005d3a","handlingStrategy":"validation","validationCode":"// verify the target profile exists and does not redirect before automating\nconst res = await fetch(`https://www.xiaohongshu.com/user/profile/${id}`, { redirect: 'manual', headers: { 'user-agent': 'Mozilla/5.0' } });\nif (res.status >= 300 && res.status < 400) throw new Error('Profile redirects — ID may be invalid/deleted');","typeGuard":"function expectProfilePath(href, userId) { try { return new URL(href).pathname.match(/^\\/user\\/profile\\/([a-zA-Z0-9]{8,32})\\/?$/)?.[1] === userId; } catch { return false; } }","tryCatchPattern":"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; }","preventionTips":["Confirm the profile opens without redirect in a normal browser before automating","Keep IDs from a trusted source and re-validate stale records","Retry once on slow networks so the SPA finishes redirecting before href is sampled","Clear captcha/risk-control states before retrying"],"tags":["redirect","navigation","automation","page-state"],"backgroundTag":"unexpected-page-redirect","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}