jackwener/OpenCLI · error · ArgumentError

xiaohongshu/unfollow: user-id must be a Xiaohongshu user ID

Error message

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

What it means

This is the fallback validation in assertUserId: when the user-id argument does not look like a URL, it is treated as a raw user ID, normalized, and checked against /^[a-zA-Z0-9]{8,32}$/. If it is empty or fails that pattern, this ArgumentError is thrown, meaning the argument is neither a valid profile URL nor a plausible Xiaohongshu user ID.

Source

Thrown at clis/xiaohongshu/unfollow.js:56

    if (/^https?:\/\//i.test(input)) {
        let parsed;
        try {
            parsed = new URL(input);
        } catch {
            throw new ArgumentError('xiaohongshu/unfollow: invalid profile URL');
        }
        if (parsed.protocol !== 'https:' || !isXiaohongshuHost(parsed.hostname)) {
            throw new ArgumentError('xiaohongshu/unfollow: 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/unfollow: 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/unfollow: user-id must be a Xiaohongshu user ID (e.g. 5d8f88dc0000000001005d3a) or full profile URL',
        );
    }
    return userId;
}

/**
 * Click the 已关注 / 已互关 button on the profile (idempotent if not following).
 * Returns `{ ok, state }` where state is 'unfollow-clicked' | 'not-following' | 'failed'.
 */
function buildClickUnfollowScript() {
    return `
(() => {
  const FOLLOW_LABELS = ['关注', '+ 关注', '+关注'];
  const FOLLOWING_LABELS = ['已关注', '已互关', '互相关注'];

  const isVisible = (el) => !!el && el.offsetParent !== null;
  const textOf = (el) => (el.innerText || el.textContent || '').trim();

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Get the true profile ID: open the user's page and copy the last path segment of /user/profile/<id> (e.g. 5d8f88dc0000000001005d3a) — not the 小红书号 shown on the profile card
  2. Pass the full profile URL (with https://) if you do not want to extract the ID manually
  3. Strip whitespace, hyphens, and any 'user:' prefixes from the value before passing it
  4. Ensure the argument is non-empty and 8-32 alphanumeric characters

Example fix

// before
$ xiaohongshu unfollow '小小美食家'   # nickname, not the ID
// after
$ xiaohongshu unfollow '5d8f88dc0000000001005d3a'
Defensive patterns

Strategy: validation

Validate before calling

const USER_ID_RE = /^[a-zA-Z0-9]{8,32}$/;
const id = String(arg ?? '').trim();
if (!USER_ID_RE.test(id) && !/^https?:\/\//.test(id)) throw new Error('Invalid xiaohongshu user id: ' + id);

Type guard

function isValidXhsUserId(v) { return /^[a-zA-Z0-9]{8,32}$/.test(String(v ?? '').trim()); }

Try / catch

try { await cli.unfollow({ 'user-id': id }); } catch (e) { if (String(e.message).includes('user-id must be')) { /* prompt for correct ID or profile URL */ } else throw e; }

Prevention

When it happens

Trigger: Passing an empty string, a display name or nickname instead of an ID, a numeric-only phone-like string, an ID containing hyphens/underscores/non-ASCII characters, an ID shorter than 8 or longer than 32 characters, or forgetting the positional argument entirely (raw becomes undefined/empty after String(raw ?? '').trim()).

Common situations: Confusing the user's nickname/RED ID (小红书号) with the internal profile ID from /user/profile/<id>; pasting a name with spaces; truncating a copied ID; passing a URL without scheme like 'www.xiaohongshu.com/user/profile/<id>' (no http prefix so it is checked as an ID and fails).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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