jackwener/OpenCLI · error · CommandExecutionError
Could not verify list removal: ${listsAfter.__error}
Error message
Could not verify list removal: ${listsAfter.__error} What it means
After clicking Save, listRemoveUser re-fetches the lists management API inside the browser to verify the removal. If that fetch returns a non-OK status, it returns {__error:'HTTP <status>'} and the library throws this error — verification could not even begin because the verification request itself failed.
Source
Thrown at clis/twitter/list-remove-core.js:243
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 };
return await r.json();
}`));
if (listsAfter && listsAfter.__error) {
throw new CommandExecutionError(`Could not verify list removal: ${listsAfter.__error}`);
}
if (!getListsManagementInstructions(listsAfter)) {
throw new CommandExecutionError('Could not verify list removal: unexpected lists payload shape');
}
const parsedAfter = parseListsManagement(listsAfter, new Set());
const afterList = parsedAfter.find((l) => l.id === listId);
if (!afterList) {
throw new CommandExecutionError(`Could not verify list removal: list ${listId} missing from post-delete payload`);
}
const memberCountAfter = Number(afterList.members) || 0;
if (memberCountAfter < memberCountBefore) {
verifiedBy = `member_count ${memberCountBefore} → ${memberCountAfter}`;
} else {
throw new CommandExecutionError(`Failed to remove @${username} from list ${listId}: member_count unchanged (${memberCountBefore} → ${memberCountAfter}).`);
}
}
return [{View on GitHub (pinned to 49907e53dc)
Solutions
- Re-authenticate: log into x.com in the controlled browser so auth_token/ct0 cookies are fresh, then retry
- Retry after a short backoff — 429/5xx verification fetches are often transient
- Check the HTTP status embedded in the message (e.g. HTTP 400 → the twitter-openapi queryId expired; update the library)
- Run `opencli twitter lists` first to confirm the API path and cookies work before attempting list mutations
Example fix
// before
await listRemoveUser(page, { listId, username });
// after
try {
await listRemoveUser(page, { listId, username });
} catch (e) {
if (/Could not verify list removal: HTTP (429|5\d\d)/.test(e.message)) {
await new Promise(r => setTimeout(r, 30000));
await listRemoveUser(page, { listId, username });
} else throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
const cookies = await page.getCookies({ url: 'https://x.com' });
if (!cookies.some(c => c.name === 'ct0') || !cookies.some(c => c.name === 'auth_token')) {
throw new Error('Log into x.com first — removal verification requires a live session.');
}
await run('twitter', 'lists', { limit: 1 }); // pre-flight: API path works Type guard
const isHttpErr = (v) => v && typeof v === 'object' && typeof v.__error === 'string';
Try / catch
try {
await listRemoveUser(page, { listId, username });
} catch (e) {
const m = /Could not verify list removal: HTTP (\d+)/.exec(e.message);
if (m && (m[1] === '429' || m[1].startsWith('5'))) {
await new Promise(r => setTimeout(r, 30000));
return listRemoveUser(page, { listId, username });
}
throw e;
} Prevention
- Log into x.com in the controlled browser before mutation commands
- Pre-flight with a read command (`twitter lists`) to confirm the session and API path
- Rate-limit your automation; back off on 429
- Keep the library updated so the fetched GraphQL queryId stays valid
When it happens
Trigger: The post-action page.evaluate fetch of listsUrl returned HTTP 401/403/404/429 etc., i.e. the response was not ok, so {__error:'HTTP '+r.status} was set and thrown here.
Common situations: Auth cookie (auth_token/ct0) expired mid-run; rate limiting from the rapid native clicks + immediate fetch; transient network failure; the lists management GraphQL queryId fetched from the fa0311 placeholder upstream became stale (HTTP 400).
Related errors
- 请先在共享 Chrome 完成 1688 登录/验证,再重试(${action})
- 1point3acres Discuz *_auth cookie missing
- 需要登录一亩三分地后再使用该命令
- API_ERROR
- API_ERROR
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e2953cd5d67841ba.
Report an issue: GitHub.