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
- 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>
- Verify the ID segment is exactly 24 hexadecimal characters (length and charset are validated by regex)
- Pass the bare 24-character ID directly as the --note argument
- 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
- Copy the note URL while viewing the note itself (/explore/<id>), not the dashboard or profile list
- Verify the ID segment is exactly 24 hex characters before calling the CLI
- Keep raw note IDs (not URLs) in scripts/automation to avoid path-shape drift
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
- --url must be a Discord channel URL like https://discord.com
- --url must be a Discord thread/post URL like https://discord
- id not a valid Grok URL (got "${input}")
- Invalid Instagram URL: ${raw}
- job-id must be a Midjourney UUID or https://www.midjourney.c
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/da80a74b0b84766b.
Report an issue: GitHub.