jackwener/OpenCLI · error · CommandExecutionError

Failed to delete draft ${row.id}

Error message

Failed to delete draft ${row.id}

What it means

Thrown by draft-delete.js when the in-page deleteDraftScript returns ok:false or no result, meaning the IndexedDB delete operation itself failed. The CLI falls back to a generic 'Failed to delete draft <id>' message when the script did not provide a more specific error.

Source

Thrown at clis/xiaohongshu/draft-delete.js:91

        const draftType = normalizeDraftType(kwargs.type);
        const execute = kwargs.execute === true;
        await ensureDraftDbPage(page);
        const entries = await readDraftEntries(page, draftType);
        const entry = findDraftEntry(entries, id);
        if (!entry) throw draftNotFound(id, draftType, 'xiaohongshu/draft-delete');
        const row = normalizeDraftRecord(entry.row, entry.key, draftType, 1);
        if (!execute) {
            return [{
                status: 'dry-run',
                id: row.id,
                type: draftType,
                message: 'Draft exists. Re-run with --execute to delete.',
            }];
        }
        const storeName = STORE_NAME_MAP[draftType];
        const result = unwrapBrowserResult(await page.evaluate(deleteDraftScript(storeName, entry.key)));
        if (!result?.ok) {
            throw new CommandExecutionError(result?.error || `Failed to delete draft ${row.id}`);
        }
        if (!result.deleted) {
            throw new CommandExecutionError(`Draft ${row.id} still exists after delete`);
        }
        return [{
            status: 'deleted',
            id: row.id,
            type: draftType,
            message: 'Draft deleted.',
        }];
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run without --execute to confirm the draft still exists and get a fresh entry.key
  2. Refresh/reconnect the browser page (ensureDraftDbPage) and retry the delete
  3. Verify STORE_NAME_MAP for the chosen draftType matches the current XHS IndexedDB schema
  4. If result.error is present in other code paths, inspect it; here the message hides it, so add logging or update the library

Example fix

// before
const result = unwrapBrowserResult(await page.evaluate(deleteDraftScript(storeName, entry.key)));
// after
if (!entry.key) throw new ArgumentError('Draft key missing; re-run lookup without --execute');
const result = unwrapBrowserResult(await page.evaluate(deleteDraftScript(storeName, entry.key)));
Defensive patterns

Strategy: try-catch

Validate before calling

const entry = await lookupDraft(page, draftType, id); // dry-run: fetches entry.key
if (!entry?.key) throw new Error(`Draft ${id} not found; nothing to delete`);

Type guard

function isDeleteResult(r) {
  return r != null && typeof r === 'object' && r.ok === true && typeof r.deleted === 'boolean';
}

Try / catch

try {
  await draftDelete({ type: 'video', id, execute: true });
} catch (e) {
  if (/Failed to delete draft/.test(e.message)) {
    console.error('Delete transaction failed — refresh the page/session and retry');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the delete command with --execute when page.evaluate(deleteDraftScript(...)) returns undefined/null or { ok: false } — e.g. the IndexedDB transaction failed, the store is missing, or the browser page was closed or navigated mid-operation.

Common situations: Draft already deleted in the UI so the record/key is stale; browser page navigated away or crashed during the evaluate; XHS updated its database schema so STORE_NAME_MAP points at a nonexistent store.

Related errors


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