{"record":{"id":"ee492e302f2a94c4","repo":"jackwener/OpenCLI","slug":"xiaohongshu-delete-note-invalid-note-url","errorCode":null,"errorMessage":"xiaohongshu/delete-note: invalid note URL","messagePattern":"xiaohongshu/delete-note: invalid note URL","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/delete-note.js","lineNumber":72,"sourceCode":"    return url.hostname.toLowerCase() === 'creator.xiaohongshu.com'\n        && url.pathname.replace(/\\/+$/, '') === '/statistics/note-detail';\n}\nfunction normalizeNoteId(input) {\n    const raw = String(input ?? '').trim();\n    if (!raw) {\n        throw new ArgumentError('xiaohongshu/delete-note: note-id cannot be empty');\n    }\n    if (NOTE_ID_RE.test(raw))\n        return raw.toLowerCase();\n    if (!/^https:\\/\\//i.test(raw)) {\n        throw new ArgumentError('xiaohongshu/delete-note: note-id must be a 24-character Xiaohongshu note ID or an exact Xiaohongshu note URL');\n    }\n    let url;\n    try {\n        url = new URL(raw);\n    }\n    catch {\n        throw new ArgumentError('xiaohongshu/delete-note: invalid note URL');\n    }\n    if (url.protocol !== 'https:' || url.username || url.password || url.port || !isXiaohongshuHost(url.hostname)) {\n        throw new ArgumentError('xiaohongshu/delete-note: note URL must be an exact https://*.xiaohongshu.com URL');\n    }\n    const queryId = url.searchParams.get('noteId') || url.searchParams.get('note_id');\n    if (queryId && NOTE_ID_RE.test(queryId) && isSupportedQueryNoteUrl(url))\n        return queryId.toLowerCase();\n    const pathMatch = url.pathname.match(/^\\/(?:explore|note|search_result|discovery\\/item)\\/([0-9a-f]{24})\\/?$/i)\n        || url.pathname.match(/^\\/user\\/profile\\/[^/?#]+\\/([0-9a-f]{24})\\/?$/i);\n    if (pathMatch)\n        return pathMatch[1].toLowerCase();\n    throw new ArgumentError('xiaohongshu/delete-note: note URL must contain a 24-character note ID');\n}\nfunction buildLocateAndMaybeDeleteScript(noteId, shouldClick) {\n    return `\n      (cfg => {\n        const { targetId, shouldClick } = cfg;\n        const isVisible = (el) => !!el && el.offsetParent !== null;","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/delete-note.js#L54-L90","documentation":"normalizeNoteId validates the note-id argument passed to the xiaohongshu/delete-note CLI. If the input is not a bare 24-hex-char note ID and not an https URL, it tries `new URL(raw)`; when that constructor throws (malformed URL), this ArgumentError is raised. It guards against silently proceeding with an unparseable URL string.","triggerScenarios":"Passing a --note argument that starts with 'https://' (or http) but is not parseable by the WHATWG URL parser — e.g. 'https://', 'https:/xiaohongshu.com/explore/x', a URL with unescaped spaces or control characters, or a truncated/mangled URL copied from a chat or terminal that wrapped lines.","commonSituations":"Copy-pasting a note link that got line-wrapped in a terminal or chat app; missing characters after shell escaping; hand-editing a URL and introducing typos; shell globbing or brace expansion mangling the URL before it reaches the CLI.","solutions":["Inspect the exact argument with `node -e \"new URL(process.argv[1])\" '<your-url>'` to see what is malformed","Re-copy the note URL directly from the browser address bar (fully expanded, including https://)","If you have the note ID itself, pass the 24-hex-character ID instead of a URL","Quote the URL in the shell so special characters (?, &, #) are not consumed by the shell"],"exampleFix":"// before\nopencli xiaohongshu delete-note --note 'https:/xiaohongshu.com/explore/abc...'\n// after\nopencli xiaohongshu delete-note --note 'https://www.xiaohongshu.com/explore/0123456789abcdef01234567'","handlingStrategy":"validation","validationCode":"function isValidNoteUrlArg(raw) {\n  const s = String(raw ?? '').trim();\n  if (/^[0-9a-f]{24}$/i.test(s)) return true;\n  if (!/^https:\\/\\//i.test(s)) return false;\n  try { new URL(s); return true; } catch { return false; }\n}\nif (!isValidNoteUrlArg(noteArg)) throw new Error('argument is not a valid note ID or URL');","typeGuard":"function isParsableUrl(s) {\n  if (typeof s !== 'string') return false;\n  try { new URL(s); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  await cli('xiaohongshu', 'delete-note', { note: noteArg });\n} catch (err) {\n  if (err instanceof ArgumentError && err.message.includes('invalid note URL')) {\n    console.error('The --note argument is not a parseable URL; pass a 24-char ID or a full https://...xiaohongshu.com link.');\n  } else throw err;\n}","preventionTips":["Always quote note URLs in the shell so ?, &, and # are not interpreted","Re-copy URLs directly from the browser address bar rather than from chat/terminal output where they may wrap","Prefer passing the bare 24-character note ID, which skips URL parsing entirely"],"tags":["argument-validation","url-parsing","cli"],"backgroundTag":"invalid-url-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}