jackwener/OpenCLI · error · CommandExecutionError

Failed to delete list ${listId}: ${deleteResult?.message ||

Error message

Failed to delete list ${listId}: ${deleteResult?.message || 'unknown UI failure'}

What it means

The twitter list-delete command drives x.com's web UI via in-page DOM automation to delete a list. When the scripted UI flow fails to reach a successful end state (any of its findButton/click steps returns { ok: false } with a message, or the browser result itself is unusable), it throws CommandExecutionError wrapping that message. This throw at list-delete.js:148 means the click-through did NOT complete — the verification step (list still present afterwards) was never reached.

Source

Thrown at clis/twitter/list-delete.js:148

            const editLink = Array.from(document.querySelectorAll('a[href$="/info"]'))
                .find((el) => visible(el) && /edit list/i.test(el.innerText || el.textContent || ''));
            if (!editLink) return { ok: false, message: 'Edit List link not found' };
            editLink.click();
            const editDialog = await waitFor(() => document.querySelector('[role="dialog"]'));
            if (!editDialog) return { ok: false, message: 'Edit List dialog did not open' };
            const deleteButton = findButton('Delete List');
            if (!deleteButton) return { ok: false, message: 'Delete List button not found' };
            deleteButton.click();
            await sleep(800);
            const confirmButton = document.querySelector('[data-testid="confirmationSheetConfirm"]')
                || findButton('Delete');
            if (!confirmButton) return { ok: false, message: 'Delete confirmation button not found' };
            confirmButton.click();
            await sleep(2500);
            return { ok: true, url: location.href };
        })()`));
        if (!deleteResult?.ok) {
            throw new CommandExecutionError(`Failed to delete list ${listId}: ${deleteResult?.message || 'unknown UI failure'}`);
        }

        const listsAfter = await getManagedLists(page, headers);
        if (listsAfter.some((list) => list.id === listId)) {
            throw new CommandExecutionError(`Failed to delete list ${listId}: list still appears in managed lists.`);
        }

        return [buildListDeleteRow({ listId, targetList })];
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command once — transient slow rendering often causes the dialog/button waits to time out
  2. Verify the listId is a list YOUR account owns by checking it appears in `opencli twitter lists`; only owned lists show the Edit List link
  3. Confirm you are on the English UI (button matching is text-based: 'Edit List', 'Delete List', 'Delete')
  4. Check x.com manually at https://x.com/i/lists/<listId> — if the UI changed, the selectors in list-delete.js need updating
  5. If deleteResult was null ('unknown UI failure'), check for page.evaluate/unwrapBrowserResult errors or headless-browser exceptions in earlier logs

Example fix

// before (fragile text-based matching)
const confirmButton = document.querySelector('[data-testid="confirmationSheetConfirm"]') || findButton('Delete');
// after (increase wait and add fallback waiting for confirm sheet instead of fixed 800ms sleep)
await sleep(1500);
const confirmButton = await waitFor(() => document.querySelector('[data-testid="confirmationSheetConfirm"]') || findButton('Delete'), { timeoutMs: 10000 });
Defensive patterns

Strategy: try-catch

Validate before calling

// Before running, confirm the list exists and is yours:
const rows = await run('opencli twitter lists');
const list = rows.find(r => r.listId === listId);
if (!list) throw new Error(`List ${listId} not in your managed lists; delete will fail in the UI flow`);

Type guard

function isUiDeleteResult(r) {
  return r !== null && typeof r === 'object' && typeof r.ok === 'boolean' && (r.ok === false ? typeof r.message === 'string' : true);
}

Try / catch

try {
  await run(`opencli twitter list-delete ${listId} --confirm true`);
} catch (e) {
  if (/Failed to delete list .*: (Edit List link not found|Edit List dialog did not open|Delete List button not found|Delete confirmation button not found)/.test(e.message)) {
    // transient UI/timing issue: retry once, else inspect selectors
  } else if (/unknown UI failure/.test(e.message)) {
    // browser evaluate returned unusable data: check headless logs
  }
  throw e;
}

Prevention

When it happens

Trigger: Any in-page failure inside the page.evaluate block on x.com/i/lists/<listId>: (1) the 'Edit List' link is not found (list page didn't render, wrong listId, layout changed); (2) the edit [role=dialog] never opens within 10s; (3) no visible 'Delete List' button in the dialog; (4) no '[data-testid=confirmationSheetConfirm]' or 'Delete' confirmation button appears after 800ms; (5) deleteResult is null/undefined after unwrapBrowserResult, yielding the 'unknown UI failure' fallback message.

Common situations: Passing a listId the account can see but not own (no Edit List link); X.com UI A/B changes renaming buttons or testids (e.g. confirmationSheetConfirm); slow page loads under flaky network so the dialog wait times out; running against a locale where button text isn't 'Delete List' in English; page.evaluate returning a wrapped/error result so deleteResult is undefined.

Related errors


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