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
- Read uiResult.message in the error text: selector-not-found → likely a DOM/testid change; 'UI error: ...' → JS exception in the page.
- Reload x.com in the controlled browser and confirm you are still logged in, then retry.
- Update the data-testid selectors in the in-page UI script to match the current x.com member-dialog markup.
- Retry when the network/site is not slow — transient render races resolve on a second attempt.
- 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
- Pin/update selector expectations after x.com UI releases.
- Run against a clean, logged-in profile with no blocking overlays.
- Allow generous waits so the dialog fully renders before clicks.
- Retry once on transient render races before escalating.
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
- Waiting for 12306 tk auth cookie
- Unexpected 12306 probe: ${JSON.stringify(probe)}
- 找不到消息输入框
- ChatGPT composer is not available on the current page.
- Could not find the ChatGPT model selector in the composer.
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/62dabc374edf3441.
Report an issue: GitHub.