jackwener/OpenCLI · error · ArgumentError
xiaohongshu/delete-note: note URL must be an exact https://*
Error message
xiaohongshu/delete-note: note URL must be an exact https://*.xiaohongshu.com URL
What it means
After parsing the URL, normalizeNoteId enforces a strict allowlist: https scheme only, no embedded username/password, no explicit port, and hostname must be xiaohongshu.com or a subdomain. Anything else fails validation — this prevents SSRF-style abuse and ensures the URL is a genuine Xiaohongshu note link.
Source
Thrown at clis/xiaohongshu/delete-note.js:75
function normalizeNoteId(input) {
const raw = String(input ?? '').trim();
if (!raw) {
throw new ArgumentError('xiaohongshu/delete-note: note-id cannot be empty');
}
if (NOTE_ID_RE.test(raw))
return raw.toLowerCase();
if (!/^https:\/\//i.test(raw)) {
throw new ArgumentError('xiaohongshu/delete-note: note-id must be a 24-character Xiaohongshu note ID or an exact Xiaohongshu note URL');
}
let url;
try {
url = new URL(raw);
}
catch {
throw new ArgumentError('xiaohongshu/delete-note: invalid note URL');
}
if (url.protocol !== 'https:' || url.username || url.password || url.port || !isXiaohongshuHost(url.hostname)) {
throw new ArgumentError('xiaohongshu/delete-note: note URL must be an exact https://*.xiaohongshu.com URL');
}
const queryId = url.searchParams.get('noteId') || url.searchParams.get('note_id');
if (queryId && NOTE_ID_RE.test(queryId) && isSupportedQueryNoteUrl(url))
return queryId.toLowerCase();
const pathMatch = url.pathname.match(/^\/(?:explore|note|search_result|discovery\/item)\/([0-9a-f]{24})\/?$/i)
|| url.pathname.match(/^\/user\/profile\/[^/?#]+\/([0-9a-f]{24})\/?$/i);
if (pathMatch)
return pathMatch[1].toLowerCase();
throw new ArgumentError('xiaohongshu/delete-note: note URL must contain a 24-character note ID');
}
function buildLocateAndMaybeDeleteScript(noteId, shouldClick) {
return `
(cfg => {
const { targetId, shouldClick } = cfg;
const isVisible = (el) => !!el && el.offsetParent !== null;
const matchesNoteId = (impressionRaw) => {
if (!impressionRaw) return false;
try {View on GitHub (pinned to 49907e53dc)
Solutions
- Use the canonical share URL form: https://www.xiaohongshu.com/explore/<24-char-id> (or /discovery/item/, /search_result/, /user/profile/<user>/<id>)
- Remove any port, username, or password from the URL; ensure the scheme is exactly https://
- Prefer passing the bare 24-character note ID directly instead of a URL
- If the URL is a short link (xhslink.com), open it in a browser and copy the final expanded xiaohongshu.com URL
Example fix
// before opencli xiaohongshu delete-note --note 'http://xiaohongshu.com:443/explore/abc' // after opencli xiaohongshu delete-note --note 'https://www.xiaohongshu.com/explore/0123456789abcdef01234567'
Defensive patterns
Strategy: validation
Validate before calling
function isCanonicalXhsUrl(raw) {
let u;
try { u = new URL(String(raw).trim()); } catch { return false; }
return u.protocol === 'https:'
&& !u.username && !u.password && !u.port
&& (u.hostname === 'xiaohongshu.com' || u.hostname.endsWith('.xiaohongshu.com'))
|| /^[0-9a-f]{24}$/i.test(String(raw).trim());
} Type guard
function isXhsHost(hostname) {
const h = String(hostname || '').toLowerCase();
return h === 'xiaohongshu.com' || h.endsWith('.xiaohongshu.com');
} Try / catch
try {
await cli('xiaohongshu', 'delete-note', { note: url });
} catch (err) {
if (err instanceof ArgumentError && err.message.includes('https://*.xiaohongshu.com')) {
console.error('Use an exact https://*.xiaohongshu.com URL (no port, no credentials) or a bare 24-char note ID.');
} else throw err;
} Prevention
- Normalize links to https://www.xiaohongshu.com/explore/<id> before invoking the CLI
- Strip any :port and user:pass@ segments from URLs constructed programmatically
- Expand short links (xhslink.com) to their final xiaohongshu.com destination first
When it happens
Trigger: Passing an http:// URL, a URL with a port (e.g. https://xiaohongshu.com:8443/...), a URL with credentials (user:pass@host), a localhost/test URL, or a link to a different domain (e.g. a short-link redirector or mirror site) instead of an exact https://*.xiaohongshu.com URL.
Common situations: Using an http link from an old bookmark; testing against a local dev mirror; passing a xiaohongshu short-link domain or third-party aggregator link; credentials accidentally embedded from a proxy config; including an explicit :443 port in the URL.
Related errors
- xiaohongshu/follow: profile URL must be an exact https://*.x
- Expected a trusted HTTPS bilibili.com video URL without cred
- ${label} must be an https LinkedIn URL without credentials o
- ${label} must point to linkedin.com
- ${label} must be an exact https://www.linkedin.com/messaging
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/73873f009eb7f9d2.
Report an issue: GitHub.