{"record":{"id":"07be47a0582f0124","repo":"jackwener/OpenCLI","slug":"xiaohongshu-unfollow-profile-url-must-be-an-exact","errorCode":null,"errorMessage":"xiaohongshu/unfollow: profile URL must be an exact https://*.xiaohongshu.com URL","messagePattern":"xiaohongshu/unfollow: profile URL must be an exact https://\\*\\.xiaohongshu\\.com URL","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/unfollow.js","lineNumber":46,"sourceCode":"function requireActionResult(payload, context) {\n    const inner = unwrapEvaluateResult(payload);\n    if (!inner || typeof inner !== 'object' || Array.isArray(inner) || typeof inner.ok !== 'boolean') {\n        throw new CommandExecutionError(`xiaohongshu/unfollow: malformed ${context} payload`);\n    }\n    return inner;\n}\n\nfunction assertUserId(raw) {\n    const input = String(raw ?? '').trim();\n    if (/^https?:\\/\\//i.test(input)) {\n        let parsed;\n        try {\n            parsed = new URL(input);\n        } catch {\n            throw new ArgumentError('xiaohongshu/unfollow: invalid profile URL');\n        }\n        if (parsed.protocol !== 'https:' || !isXiaohongshuHost(parsed.hostname)) {\n            throw new ArgumentError('xiaohongshu/unfollow: profile URL must be an exact https://*.xiaohongshu.com URL');\n        }\n        const match = parsed.pathname.match(/^\\/user\\/profile\\/([a-zA-Z0-9]{8,32})\\/?$/);\n        if (!match) {\n            throw new ArgumentError('xiaohongshu/unfollow: profile URL must be /user/profile/<userId>');\n        }\n        return match[1];\n    }\n    const userId = normalizeXhsUserId(raw);\n    if (!userId || !USER_ID_RE.test(userId)) {\n        throw new ArgumentError(\n            'xiaohongshu/unfollow: user-id must be a Xiaohongshu user ID (e.g. 5d8f88dc0000000001005d3a) or full profile URL',\n        );\n    }\n    return userId;\n}\n\n/**\n * Click the 已关注 / 已互关 button on the profile (idempotent if not following).","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/unfollow.js#L28-L64","documentation":"assertUserId accepts URL-shaped input only if it is an https URL on xiaohongshu.com or a subdomain. This ArgumentError is thrown when the URL parses but uses a non-https protocol or points at a non-Xiaohongshu host. The library restricts URL input to exact https://*.xiaohongshu.com profile pages to avoid acting on spoofed or redirector links.","triggerScenarios":"Passing 'http://www.xiaohongshu.com/user/profile/<id>' (http, not https); passing a different site's URL such as 'https://example.com/user/profile/abc' or 'https://xhslink.com/xyz' (short-link domains are rejected); passing 'https://www.xiaohongshu.com.evil.com/...'.","commonSituations":"Copying an http:// link from an old message or email; using a xiaohongshu short-link (xhslink.com) or mobile-scheme link (xhsdiscover://) instead of the canonical web profile URL; building the URL with a configurable base host that points elsewhere.","solutions":["Upgrade the URL to https:// : 'https://www.xiaohongshu.com/user/profile/<userId>'","Resolve short links (xhslink.com) in a browser and copy the final canonical xiaohongshu.com URL","Alternatively pass only the raw user ID (8-32 alphanumeric characters) instead of a URL","Check for typos in the hostname (xiaohongshu.com, not xiaohongshu.co or a mirror domain)"],"exampleFix":"// before\n$ xiaohongshu unfollow 'http://www.xiaohongshu.com/user/profile/5d8f88dc0000000001005d3a'\n// after\n$ xiaohongshu unfollow 'https://www.xiaohongshu.com/user/profile/5d8f88dc0000000001005d3a'","handlingStrategy":"validation","validationCode":"function assertCanonicalXhsUrl(input) {\n  const u = new URL(String(input).trim());\n  if (u.protocol !== 'https:' || !(u.hostname === 'xiaohongshu.com' || u.hostname.endsWith('.xiaohongshu.com'))) {\n    throw new Error('URL must be https://*.xiaohongshu.com: ' + input);\n  }\n  return u;\n}","typeGuard":"function isXhsUrl(input) {\n  try { const u = new URL(String(input).trim());\n    return u.protocol === 'https:' && (u.hostname === 'xiaohongshu.com' || u.hostname.endsWith('.xiaohongshu.com'));\n  } catch { return false; }\n}","tryCatchPattern":"try { await cli.unfollow({ 'user-id': url }); } catch (e) { if (String(e.message).includes('https://*.xiaohongshu.com')) { /* resolve short link or upgrade to https and retry once */ } else throw e; }","preventionTips":["Always use https:// canonical xiaohongshu.com profile URLs","Expand xhslink.com short links before passing them","Never pass third-party or mirror domains","Pass the raw user ID to bypass URL checks entirely"],"tags":["argument-validation","url-parsing","https"],"backgroundTag":"invalid-url-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}