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

  1. Use the canonical share URL form: https://www.xiaohongshu.com/explore/<24-char-id> (or /discovery/item/, /search_result/, /user/profile/<user>/<id>)
  2. Remove any port, username, or password from the URL; ensure the scheme is exactly https://
  3. Prefer passing the bare 24-character note ID directly instead of a URL
  4. 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

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


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