jackwener/OpenCLI · error · ArgumentError

xiaohongshu/follow: user-id must be a Xiaohongshu user ID (e

Error message

xiaohongshu/follow: user-id must be a Xiaohongshu user ID (e.g. 5d8f88dc0000000001005d3a) or full profile URL

What it means

The catch-all branch of assertUserId: the input was neither a valid URL nor an ID matching USER_ID_RE after normalizeXhsUserId, so ArgumentError is thrown. It tells the caller the accepted formats: a Xiaohongshu user ID like 5d8f88dc0000000001005d3a, or a full profile URL.

Source

Thrown at clis/xiaohongshu/follow.js:60

    if (/^https?:\/\//i.test(input)) {
        let parsed;
        try {
            parsed = new URL(input);
        } catch {
            throw new ArgumentError('xiaohongshu/follow: invalid profile URL');
        }
        if (parsed.protocol !== 'https:' || !isXiaohongshuHost(parsed.hostname)) {
            throw new ArgumentError('xiaohongshu/follow: profile URL must be an exact https://*.xiaohongshu.com URL');
        }
        const match = parsed.pathname.match(/^\/user\/profile\/([a-zA-Z0-9]{8,32})\/?$/);
        if (!match) {
            throw new ArgumentError('xiaohongshu/follow: profile URL must be /user/profile/<userId>');
        }
        return match[1];
    }
    const userId = normalizeXhsUserId(raw);
    if (!userId || !USER_ID_RE.test(userId)) {
        throw new ArgumentError(
            'xiaohongshu/follow: user-id must be a Xiaohongshu user ID (e.g. 5d8f88dc0000000001005d3a) or full profile URL',
        );
    }
    return userId;
}

/**
 * The injected page script. Lives in the browser context, so it can't import
 * anything — every helper is inlined. Returns `{ ok, state, reason? }` where
 * `state` is one of: 'followed' | 'already-following' | 'failed'.
 */
function buildFollowScript() {
    return `
(async () => {
  const FOLLOW_LABELS = ['关注', '+ 关注', '+关注'];
  const FOLLOWING_LABELS = ['已关注', '已互关', '互相关注'];
  const STATE_FLIP_TIMEOUT_MS = ${STATE_FLIP_TIMEOUT_MS};
  const STATE_POLL_MS = 250;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Open the target user's profile in a browser and copy the 24-hex-char segment from /user/profile/<id>.
  2. Pass that raw ID: opencli xiaohongshu follow --user-id 5d8f88dc0000000001005d3a.
  3. If you only have a nickname or short link, resolve it to the profile URL first, then pass the full https URL.

Example fix

// before
opencli xiaohongshu follow --user-id '张三的小红书'
// after
opencli xiaohongshu follow --user-id '5d8f88dc0000000001005d3a'
Defensive patterns

Strategy: validation

Validate before calling

const USER_ID_RE = /^[a-zA-Z0-9]{8,32}$/;
function isValidXhsUserId(raw) {
  const s = String(raw ?? '').trim();
  return USER_ID_RE.test(s) || /^https:\/\/([a-z0-9-]+\.)?xiaohongshu\.com\/user\/profile\/[a-zA-Z0-9]{8,32}\/?$/i.test(s);
}

Try / catch

try {
  await opencliFollow(userArg);
} catch (err) {
  if (err.code === 'ARGUMENT' && err.message.includes('user-id must be')) {
    console.error('Expected a Xiaohongshu user ID (hex) or full profile URL');
    process.exitCode = 2;
  } else throw err;
}

Prevention

When it happens

Trigger: Passing a display name or nickname instead of an ID ('--user-id 小红书用户'), a numeric-only short ID, an ID with hyphens/underscores, or an empty/whitespace string to the follow command.

Common situations: Assuming the CLI accepts usernames (it requires the hex-style ID); grabbing the wrong number from a note URL (note id vs user id); shell eating quotes so the value arrives empty; using an old-format ID that no longer matches the ID regex.

Related errors


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