{"record":{"id":"da80a74b0b84766b","repo":"jackwener/OpenCLI","slug":"xiaohongshu-delete-note-note-url-must-contain-a-2","errorCode":null,"errorMessage":"xiaohongshu/delete-note: note URL must contain a 24-character note ID","messagePattern":"xiaohongshu/delete-note: note URL must contain a 24-character note ID","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/delete-note.js","lineNumber":84,"sourceCode":"    }\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;\n        const matchesNoteId = (impressionRaw) => {\n          if (!impressionRaw) return false;\n          try {\n            const parsed = JSON.parse(impressionRaw);\n            const id = parsed && parsed.noteTarget && parsed.noteTarget.value && parsed.noteTarget.value.noteId;\n            return typeof id === 'string' && id === targetId;\n          } catch {\n            return false;\n          }\n        };\n        const notes = Array.from(document.querySelectorAll('.note')).filter(isVisible);\n        for (const note of notes) {","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/delete-note.js#L66-L102","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nopencli xiaohongshu delete-note --note 'https://www.xiaohongshu.com/user/profile/5ff0da6b0000000001008400'\n// after\nopencli xiaohongshu delete-note --note 'https://www.xiaohongshu.com/explore/0123456789abcdef01234567'","handlingStrategy":"validation","validationCode":"const NOTE_ID_RE = /^[0-9a-f]{24}$/i;\nfunction extractNoteId(raw) {\n  const s = String(raw ?? '').trim();\n  if (NOTE_ID_RE.test(s)) return s.toLowerCase();\n  try {\n    const u = new URL(s);\n    const m = u.pathname.match(/^\\/(?:explore|note|search_result|discovery\\/item)\\/([0-9a-f]{24})\\/?$/i)\n      || u.pathname.match(/^\\/user\\/profile\\/[^/?#]+\\/([0-9a-f]{24})\\/?$/i);\n    if (m) return m[1].toLowerCase();\n  } catch {}\n  return null;\n}\nif (!extractNoteId(noteArg)) throw new Error('no 24-char note ID found in argument');","typeGuard":"function hasNoteId(u) {\n  return /^\\/(?:explore|note|search_result|discovery\\/item)\\/([0-9a-f]{24})\\/?$/i.test(u.pathname)\n    || /^\\/user\\/profile\\/[^/?#]+\\/([0-9a-f]{24})\\/?$/i.test(u.pathname);\n}","tryCatchPattern":"try {\n  await cli('xiaohongshu', 'delete-note', { note: noteArg });\n} catch (err) {\n  if (err instanceof ArgumentError && err.message.includes('24-character note ID')) {\n    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.');\n  } else throw err;\n}","preventionTips":["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"],"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"}