jackwener/OpenCLI · error · CommandExecutionError
Draft ${row.id} still exists after delete
Error message
Draft ${row.id} still exists after delete What it means
Thrown by draft-delete.js when the delete script reports ok but result.deleted is falsy, meaning the script completed yet the draft record is still present in the store. This is a post-condition check to guarantee the delete actually took effect.
Source
Thrown at clis/xiaohongshu/draft-delete.js:94
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
- Re-run the command without --execute to refresh the entry key, then delete again
- Check key type handling in encodeDraftKey/deleteDraftScript (s: string vs numeric keys)
- Verify the object store's keyPath in DevTools matches what the script queries
- Report/update the library if XHS changed its draft store key structure
Example fix
// before
const result = unwrapBrowserResult(await page.evaluate(deleteDraftScript(storeName, entry.key)));
if (!result.deleted) throw new CommandExecutionError(`Draft ${row.id} still exists after delete`);
// after
// refresh entry.key with a fresh dry-run lookup first:
const fresh = await lookupDraft(page, draftType, row.id); // no --execute
const result = unwrapBrowserResult(await page.evaluate(deleteDraftScript(storeName, fresh.key))); Defensive patterns
Strategy: retry
Validate before calling
const entry = await lookupDraft(page, draftType, id);
if (!entry?.key) throw new Error(`Draft ${id} already gone`); Type guard
function wasDeleted(r) {
return r != null && typeof r === 'object' && r.ok === true && r.deleted === true;
} Try / catch
for (let attempt = 0; attempt < 3; attempt++) {
try { await draftDelete({ type, id, execute: true }); break; }
catch (e) {
if (!/still exists after delete/.test(e.message)) throw e;
await lookupDraft(page, type, id); // refresh key before retrying
}
} Prevention
- Refresh the entry key (dry-run lookup) between delete attempts
- Confirm the draft store's keyPath/key type matches encodeDraftKey's s: string encoding
- Avoid mutating drafts in the XHS UI while the delete command runs
- Check for XHS schema changes if this error starts appearing consistently
When it happens
Trigger: Running delete with --execute when deleteDraftScript returns { ok: true, deleted: false } — typically because the record with entry.key no longer matched at verification time, the key encoding (encodeDraftKey) does not match the actual key type (string vs number), or the transaction committed without removing the record.
Common situations: Key type mismatch: IndexedDB keys are numeric but the script looked up a string key; drafts refreshed between lookup and delete; XHS schema change altering the primary key path.
Related errors
- ${result.after} ${draftType} drafts still exist after clear
- Failed to delete draft ${row.id}
- Failed to ${execute ? 'clear' : 'count'} ${draftType} drafts
- Not a git repository
- Working tree not clean: ${status}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/1ccb2f7dd241d674.
Report an issue: GitHub.