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
- 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
- Pass the full profile URL (with https://) if you do not want to extract the ID manually
- Strip whitespace, hyphens, and any 'user:' prefixes from the value before passing it
- 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
- Use the internal profile ID from /user/profile/<id>, not the nickname or 小红书号
- Validate IDs with /^[a-zA-Z0-9]{8,32}$/ before calling
- Never pass the positional argument empty or undefined
- Wrap scheme-less URLs in https:// or strip them down to the ID
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
- coingecko limit must be a positive integer
- Unknown sort "${sortKey}". Valid: ${Object.keys(SORTS).join(
- INVALID_ARGUMENT
- unknown mode "${mode}"
- Unknown tag: ${value}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/6e2c3e74ebd5fee8.
Report an issue: GitHub.