jackwener/OpenCLI · error · CommandExecutionError

Failed to remove @${username} from list ${listId}: ${uiResul

Error message

Failed to remove @${username} from list ${listId}: ${uiResult.message}

What it means

The actual removal is attempted through the x.com UI inside the page (opening the member dialog, clicking the remove control). The in-page script reports failure as { ok: false, message }, and this CommandExecutionError wraps that message. It means the DOM-driven removal flow did not complete — a selector went missing, a click did not land, or the page threw inside evaluate.

Source

Thrown at clis/twitter/list-remove-core.js:221

                    return /^(Save|Done|保存|完成|儲存)$/i.test(txt);
                });
                const saveRect = saveButton ? saveButton.getBoundingClientRect() : null;
                return {
                    ok: true,
                    needsNativeInteraction: true,
                    rowClickX: Math.round(rowRect.left + rowRect.width / 2),
                    rowClickY: Math.round(rowRect.top + rowRect.height / 2),
                    saveClickX: saveRect ? Math.round(saveRect.left + saveRect.width / 2) : null,
                    saveClickY: saveRect ? Math.round(saveRect.top + saveRect.height / 2) : null,
                    mutationsBefore: window.__opencliListMutations.length,
                };
            } catch (e) {
                return { ok: false, message: 'UI error: ' + (e?.message || String(e)) };
            }
        })()`));

        if (!uiResult.ok) {
            throw new CommandExecutionError(`Failed to remove @${username} from list ${listId}: ${uiResult.message}`);
        }

        let verifiedBy = null;
        if (uiResult.needsNativeInteraction) {
            if (typeof page.nativeClick !== 'function') {
                throw new CommandExecutionError('Requires up-to-date Chrome extension (nativeClick).');
            }
            if (!uiResult.saveClickX) {
                throw new CommandExecutionError('Save button not found in dialog.');
            }
            const memberCountBefore = Number(targetList.members) || 0;
            await page.nativeClick(uiResult.rowClickX, uiResult.rowClickY);
            await new Promise((r) => setTimeout(r, 800));
            await page.nativeClick(uiResult.saveClickX, uiResult.saveClickY);
            await new Promise((r) => setTimeout(r, 3500));
            const listsAfter = unwrapBrowserResult(await page.evaluate(`async () => {
                const r = await fetch(${JSON.stringify(listsUrl)}, { headers: ${headers}, credentials: 'include' });
                if (!r.ok) return { __error: 'HTTP ' + r.status };

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read uiResult.message in the error text: selector-not-found → likely a DOM/testid change; 'UI error: ...' → JS exception in the page.
  2. Reload x.com in the controlled browser and confirm you are still logged in, then retry.
  3. Update the data-testid selectors in the in-page UI script to match the current x.com member-dialog markup.
  4. Retry when the network/site is not slow — transient render races resolve on a second attempt.
  5. Dismiss any overlay/interstitial dialogs shown on x.com before running the command.

Example fix

// before (row selector outdated after x.com redesign)
const row = document.querySelector(`[data-testid="UserCell"]`);
// after
const row = document.querySelector('[data-testid="ListItem"], [data-testid="UserCell"]'); // support both old and new markup
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the profile page is interactive before invoking the UI flow
await page.goto(`https://x.com/${username}`);
await page.wait({ selector: '[data-testid="primaryColumn"]' });
const overlay = await page.evaluate("document.querySelector('[data-testid="sheetDialog"], [aria-label="Log in"]')");
if (overlay) throw new Error('Blocking overlay present on x.com; resolve it first');

Type guard

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

Try / catch

try {
  await listRemoveUser(page, { listId, username });
} catch (e) {
  if (e instanceof CommandExecutionError && e.message.startsWith('Failed to remove')) {
    console.warn('UI flow failed:', e.message);
    // reload page, dismiss overlays, update selectors, retry once
  } else throw e;
}

Prevention

When it happens

Trigger: The in-page UI routine throws (wrapped as 'UI error: ...'), the list member row or remove/confirm buttons are not found in the dialog, an unexpected overlay (login prompt, rate-limit notice, JS error dialog) intercepts the flow, or x.com's DOM changed so data-testid selectors no longer match.

Common situations: x.com shipped a UI redesign changing data-testid attributes; slow page load left the dialog half-rendered when clicking; a modal/interstitial (login, sensitive-content, outage banner) blocked interaction; the account got logged out mid-flow; network slowness caused element detach between locate and click.

Related errors


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