{"record":{"id":"73324d648151a07b","repo":"jackwener/OpenCLI","slug":"xiaohongshu-follow-profile-url-must-be-user-prof","errorCode":null,"errorMessage":"xiaohongshu/follow: profile URL must be /user/profile/<userId>","messagePattern":"xiaohongshu/follow: profile URL must be /user/profile/<userId>","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/follow.js","lineNumber":54,"sourceCode":"    }\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/follow: invalid profile URL');\n        }\n        if (parsed.protocol !== 'https:' || !isXiaohongshuHost(parsed.hostname)) {\n            throw new ArgumentError('xiaohongshu/follow: 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/follow: 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/follow: user-id must be a Xiaohongshu user ID (e.g. 5d8f88dc0000000001005d3a) or full profile URL',\n        );\n    }\n    return userId;\n}\n\n/**\n * The injected page script. Lives in the browser context, so it can't import\n * anything — every helper is inlined. Returns `{ ok, state, reason? }` where\n * `state` is one of: 'followed' | 'already-following' | 'failed'.\n */\nfunction buildFollowScript() {","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/follow.js#L36-L72","documentation":"After validating scheme and host, assertUserId extracts the user ID from the path with /\\/user\\/profile\\/([a-zA-Z0-9]{8,32})\\/?$/. If the path doesn't match, ArgumentError is thrown. The URL is valid xiaohongshu.com but not a user profile page.","triggerScenarios":"Passing https://www.xiaohongshu.com/explore/<noteId> or /user/<something> (wrong path shape), a profile URL with extra segments like /user/profile/<id>/tabs, a userId shorter than 8 or longer than 32 chars, or a userId containing '-' or '_' (non-alphanumeric).","commonSituations":"Confusing note URLs with profile URLs; using mobile app share URLs with query junk in the path; URLs to /user/profile/ landing without an id; trailing content after the id beyond an optional slash.","solutions":["Use the exact /user/profile/<userId> path: https://www.xiaohongshu.com/user/profile/5d8f88dc0000000001005d3a.","Copy the ID directly from the profile page URL while viewing the user, stripping extra path segments.","Pass the bare user ID instead of a URL to bypass path parsing entirely."],"exampleFix":"// before\nopencli xiaohongshu follow --user-id 'https://www.xiaohongshu.com/user/profile/5d8f88dc0000000001005d3a/tabs'\n// after\nopencli xiaohongshu follow --user-id '5d8f88dc0000000001005d3a'","handlingStrategy":"validation","validationCode":"const USER_PROFILE_PATH = /^\\/user\\/profile\\/([a-zA-Z0-9]{8,32})\\/?$/;\nfunction extractXhsUserId(raw) {\n  const u = new URL(String(raw));\n  const m = u.pathname.match(USER_PROFILE_PATH);\n  return m ? m[1] : null;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await opencliFollow(url);\n} catch (err) {\n  if (err.code === 'ARGUMENT' && err.message.includes('/user/profile/<userId>')) {\n    // wrong path shape — extract id or pass bare id\n  }\n  throw err;\n}","preventionTips":["Use only /user/profile/<id> URLs, not note or tab URLs","Strip extra path segments and query strings","Pass the bare user ID to bypass URL path parsing"],"tags":["argument-validation","url-parsing","cli-usage","xiaohongshu"],"backgroundTag":"invalid-url-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}