{"record":{"id":"7e4fea06ed9c0b04","repo":"jackwener/OpenCLI","slug":"xiaohongshu-delete-note-note-noteid-row-found","errorCode":null,"errorMessage":"xiaohongshu/delete-note: note ${noteId} row found but no delete action visible; xhs creator UI may have changed.","messagePattern":"xiaohongshu/delete-note: note (.+?) row found but no delete action visible; xhs creator UI may have changed\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/delete-note.js","lineNumber":201,"sourceCode":"        return false;\n      }\n    `)), 'published-tab');\n            if (!tabClicked) {\n                throw new CommandExecutionError('xiaohongshu/delete-note: 已发布 tab not found on note-manager; xhs creator UI may have changed.');\n            }\n            await page.wait({ time: ROW_SETTLE_MS / 1000 });\n            // Step 2: locate the .note row whose data-impression JSON carries the\n            // exact `noteId` field. Dry-run stops here; execute clicks delete.\n            // Substring matching on the raw attribute would risk matching unrelated\n            // fields whose values happen to share the noteId prefix, so parse the JSON\n            // and compare `noteTarget.value.noteId` explicitly.\n            const initResult = requireActionResult(unwrapEvaluateResult(await page.evaluate(buildLocateAndMaybeDeleteScript(noteId, execute))), 'locate-note');\n            if (!initResult?.ok) {\n                if (initResult?.kind === 'not_found') {\n                    throw new EmptyResultError('xiaohongshu/delete-note', `Note ${noteId} not visible in the 已发布 tab. Verify the note belongs to the logged-in account and has cleared review (审核中 / 未通过 rows have no web delete entry).`);\n                }\n                if (initResult?.kind === 'no_delete_action') {\n                    throw new CommandExecutionError(`xiaohongshu/delete-note: note ${noteId} row found but no delete action visible; xhs creator UI may have changed.`);\n                }\n                throw new CommandExecutionError('xiaohongshu/delete-note: failed to locate note row');\n            }\n            if (!execute) {\n                return [{ status: 'dry-run', note_id: noteId, message: 'Target note row and delete action verified. Re-run with --execute to delete.' }];\n            }\n            await page.wait({ time: MODAL_SETTLE_MS / 1000 });\n            // Step 3: click \"确定\" in the `.d-modal-footer` confirmation modal.\n            const confirmResult = requireActionResult(unwrapEvaluateResult(await page.evaluate(`\n      () => {\n        const isVisible = (el) => !!el && el.offsetParent !== null;\n        const footer = Array.from(document.querySelectorAll('.d-modal-footer')).find(isVisible);\n        if (!footer) return { ok: false, kind: 'no_modal' };\n        const buttons = Array.from(footer.querySelectorAll('button, [role=\"button\"]')).filter(isVisible);\n        const confirmBtn = buttons.find((b) => (b.innerText || b.textContent || '').trim() === '确定');\n        if (!confirmBtn) return { ok: false, kind: 'no_confirm', labels: buttons.map(b => (b.innerText || '').trim()) };\n        confirmBtn.click();\n        return { ok: true };","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/delete-note.js#L183-L219","documentation":"The note row was located, but the injected script could not find a visible delete action (the inline `<span class=\"control data-del\">` or equivalent hover control) on that row. The CLI raises CommandExecutionError because deletion cannot proceed without the action; this usually means XHS changed the row-action markup or the control is rendered under a state the script doesn't handle.","triggerScenarios":"XHS renamed/removed the .control.data-del element or changed its visibility conditions (e.g. requiring hover first in a new way); a UI variant renders actions differently; the row is in a state where no delete action exists despite being on the Published tab; element considered invisible (offsetParent null) due to layout changes.","commonSituations":"XHS frontend update changing action-button markup; per-account A/B layouts; browser window too narrow so action buttons collapse into an overflow menu; CSS changes making the control invisible to the offsetParent check.","solutions":["Re-run the command (hover-state timing can be transient); try a wider/maximized browser window so inline actions render","Inspect the row in DevTools to see whether the delete control still exists; if markup changed, update the library/selector logic","Update the opencli package to the latest version that tracks the current XHS UI","If no web delete entry truly exists for this note state, delete via the XHS mobile app instead"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-check the row exposes a delete control before invoking:\nconst hasDelete = await page.evaluate(`(id) => {\n  for (const row of document.querySelectorAll('.note')) {\n    try {\n      const j = JSON.parse(row.getAttribute('data-impression') || '');\n      if (j?.noteTarget?.value?.noteId === id) {\n        return !!row.querySelector('.control.data-del');\n      }\n    } catch {}\n  }\n  return false;\n}`, noteId);\nif (!hasDelete) throw new Error('delete action not visible on row');","typeGuard":null,"tryCatchPattern":"try {\n  await cli('xiaohongshu', 'delete-note', { note: noteId });\n} catch (err) {\n  if (err instanceof CommandExecutionError && err.message.includes('no delete action visible')) {\n    await sleep(2000); // hover/settle then retry once\n    return cli('xiaohongshu', 'delete-note', { note: noteId });\n  }\n  throw err;\n}","preventionTips":["Maximize the browser window so inline row actions render instead of collapsing into overflow menus","Retry once before treating it as a UI markup change","Keep the opencli package current; XHS changes the delete-control markup periodically","If the note state genuinely has no web delete entry, fall back to the mobile app"],"tags":["ui-automation","dom-change","browser-automation"],"backgroundTag":"ui-selector-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}