jackwener/OpenCLI · error · CommandExecutionError

xiaohongshu/delete-note: confirmation modal step failed (${c

Error message

xiaohongshu/delete-note: confirmation modal step failed (${confirmResult?.kind ?? 'unknown'})

What it means

Step 3 clicks 确定 (Confirm) in the .d-modal-footer confirmation modal after clicking delete. The script reported ok:false with a kind (e.g. 'no_modal', 'no_confirm') or an unknown shape, so the CLI throws this CommandExecutionError naming the failing kind. The delete was initiated but never confirmed, so nothing was deleted.

Source

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

            if (!execute) {
                return [{ status: 'dry-run', note_id: noteId, message: 'Target note row and delete action verified. Re-run with --execute to delete.' }];
            }
            await page.wait({ time: MODAL_SETTLE_MS / 1000 });
            // Step 3: click "确定" in the `.d-modal-footer` confirmation modal.
            const confirmResult = requireActionResult(unwrapEvaluateResult(await page.evaluate(`
      () => {
        const isVisible = (el) => !!el && el.offsetParent !== null;
        const footer = Array.from(document.querySelectorAll('.d-modal-footer')).find(isVisible);
        if (!footer) return { ok: false, kind: 'no_modal' };
        const buttons = Array.from(footer.querySelectorAll('button, [role="button"]')).filter(isVisible);
        const confirmBtn = buttons.find((b) => (b.innerText || b.textContent || '').trim() === '确定');
        if (!confirmBtn) return { ok: false, kind: 'no_confirm', labels: buttons.map(b => (b.innerText || '').trim()) };
        confirmBtn.click();
        return { ok: true };
      }
    `)), 'confirm-modal');
            if (!confirmResult?.ok) {
                throw new CommandExecutionError(`xiaohongshu/delete-note: confirmation modal step failed (${confirmResult?.kind ?? 'unknown'})`);
            }
            // Step 4: poll for row removal (proves the delete actually committed,
            // not just the modal was clicked). Iteration-bounded rather than
            // wall-clock so tests with a mocked `page.wait` exhaust the loop
            // quickly instead of stalling on real time.
            const VERIFY_ITERATIONS = Math.ceil(VERIFY_TIMEOUT_MS / VERIFY_POLL_MS);
            let stillPresent = true;
            for (let i = 0; i < VERIFY_ITERATIONS; i++) {
                await page.wait({ time: VERIFY_POLL_MS / 1000 });
                const probe = requireEvaluateBoolean(unwrapEvaluateResult(await page.evaluate(buildVerifyGoneScript(noteId))), 'verify-gone');
                if (probe === false) {
                    stillPresent = false;
                    break;
                }
            }
            if (stillPresent) {
                throw new CommandExecutionError(`xiaohongshu/delete-note: note ${noteId} still visible after confirm click; deletion may not have committed.`);
            }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command; if the modal was just slow, a retry may succeed (consider increasing MODAL_SETTLE_MS if configurable)
  2. Inspect the confirm modal in DevTools: if .d-modal-footer or the 确定 button markup changed, update the selector logic in the library
  3. Update the opencli package to the latest version tracking the current XHS UI
  4. Maximize the browser window / reset zoom so the modal renders normally
  5. IMPORTANT: because the delete was initiated but not confirmed, verify in the browser whether the note still exists before retrying to avoid double-delete attempts
Defensive patterns

Strategy: retry

Try / catch

try {
  await cli('xiaohongshu', 'delete-note', { note: noteId });
} catch (err) {
  if (err instanceof CommandExecutionError && err.message.includes('confirmation modal step failed')) {
    // Deletion was initiated but NOT confirmed — verify current state before retrying
    const stillThere = await noteStillListed(noteId);
    if (stillThere) return cli('xiaohongshu', 'delete-note', { note: noteId });
    return; // already deleted
  }
  throw err;
}

Prevention

When it happens

Trigger: The confirmation modal did not appear within MODAL_SETTLE_MS (2s); XHS changed the modal markup (.d-modal-footer class removed/renamed) so no confirm button is found; the confirm button label no longer matches 确定; the modal rendered but buttons were invisible to the offsetParent check.

Common situations: Slow rendering past the 2s modal settle window; XHS frontend update altering modal classes; browser window/zoom making the modal render off-layout; a different dialog variant (A/B test) with different button markup.

Related errors


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