jackwener/OpenCLI · error · ArgumentError

xiaohongshu/delete-note: note URL must contain a 24-characte

Error message

xiaohongshu/delete-note: note URL must contain a 24-character note ID

What it means

The URL parsed and passed the host check, but normalizeNoteId could not extract a 24-hex-character note ID from it. Only specific URL shapes carry the note ID: /explore/<id>, /note/<id>, /search_result/<id>, /discovery/item/<id>, /user/profile/<user>/<id>, or the creator statistics note-detail URL with ?noteId=/?note_id=. Any other path shape (or an ID of wrong length/charset) reaches this final throw.

Source

Thrown at clis/xiaohongshu/delete-note.js:84

    }
    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 {
            const parsed = JSON.parse(impressionRaw);
            const id = parsed && parsed.noteTarget && parsed.noteTarget.value && parsed.noteTarget.value.noteId;
            return typeof id === 'string' && id === targetId;
          } catch {
            return false;
          }
        };
        const notes = Array.from(document.querySelectorAll('.note')).filter(isVisible);
        for (const note of notes) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Open the note in a browser and copy the URL whose path is /explore/<24-char-id>, or grab the ID from creator.xiaohongshu.com/statistics/note-detail?noteId=<24-char-id>
  2. Verify the ID segment is exactly 24 hexadecimal characters (length and charset are validated by regex)
  3. Pass the bare 24-character ID directly as the --note argument
  4. Strip trailing/extra fragments and query params from the URL before passing it

Example fix

// before
opencli xiaohongshu delete-note --note 'https://www.xiaohongshu.com/user/profile/5ff0da6b0000000001008400'
// after
opencli xiaohongshu delete-note --note 'https://www.xiaohongshu.com/explore/0123456789abcdef01234567'
Defensive patterns

Strategy: validation

Validate before calling

const NOTE_ID_RE = /^[0-9a-f]{24}$/i;
function extractNoteId(raw) {
  const s = String(raw ?? '').trim();
  if (NOTE_ID_RE.test(s)) return s.toLowerCase();
  try {
    const u = new URL(s);
    const m = u.pathname.match(/^\/(?:explore|note|search_result|discovery\/item)\/([0-9a-f]{24})\/?$/i)
      || u.pathname.match(/^\/user\/profile\/[^/?#]+\/([0-9a-f]{24})\/?$/i);
    if (m) return m[1].toLowerCase();
  } catch {}
  return null;
}
if (!extractNoteId(noteArg)) throw new Error('no 24-char note ID found in argument');

Type guard

function hasNoteId(u) {
  return /^\/(?:explore|note|search_result|discovery\/item)\/([0-9a-f]{24})\/?$/i.test(u.pathname)
    || /^\/user\/profile\/[^/?#]+\/([0-9a-f]{24})\/?$/i.test(u.pathname);
}

Try / catch

try {
  await cli('xiaohongshu', 'delete-note', { note: noteArg });
} catch (err) {
  if (err instanceof ArgumentError && err.message.includes('24-character note ID')) {
    console.error('The URL path does not contain a 24-hex-char note ID; open the note and copy /explore/<id> or pass the ID directly.');
  } else throw err;
}

Prevention

When it happens

Trigger: Passing the note-manager list URL, the creator home page, a URL where the ID is truncated or uppercase-non-hex, an /explore/ URL whose path segment is not exactly 24 hex characters, or a query-string noteId on a hostname/path other than creator.xiaohongshu.com/statistics/note-detail.

Common situations: Copying the wrong URL (the dashboard instead of a specific note); XHS app share links with a different path format or shortened ID; appending extra query/path segments that break the regex anchors (trailing text after the ID); regional mirror domains with the ID in an unsupported path.

Related errors


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