{"record":{"id":"e2953cd5d67841ba","repo":"jackwener/OpenCLI","slug":"could-not-verify-list-removal-listsafter-erro","errorCode":null,"errorMessage":"Could not verify list removal: ${listsAfter.__error}","messagePattern":"Could not verify list removal: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/list-remove-core.js","lineNumber":243,"sourceCode":"        if (uiResult.needsNativeInteraction) {\n            if (typeof page.nativeClick !== 'function') {\n                throw new CommandExecutionError('Requires up-to-date Chrome extension (nativeClick).');\n            }\n            if (!uiResult.saveClickX) {\n                throw new CommandExecutionError('Save button not found in dialog.');\n            }\n            const memberCountBefore = Number(targetList.members) || 0;\n            await page.nativeClick(uiResult.rowClickX, uiResult.rowClickY);\n            await new Promise((r) => setTimeout(r, 800));\n            await page.nativeClick(uiResult.saveClickX, uiResult.saveClickY);\n            await new Promise((r) => setTimeout(r, 3500));\n            const listsAfter = unwrapBrowserResult(await page.evaluate(`async () => {\n                const r = await fetch(${JSON.stringify(listsUrl)}, { headers: ${headers}, credentials: 'include' });\n                if (!r.ok) return { __error: 'HTTP ' + r.status };\n                return await r.json();\n            }`));\n            if (listsAfter && listsAfter.__error) {\n                throw new CommandExecutionError(`Could not verify list removal: ${listsAfter.__error}`);\n            }\n            if (!getListsManagementInstructions(listsAfter)) {\n                throw new CommandExecutionError('Could not verify list removal: unexpected lists payload shape');\n            }\n            const parsedAfter = parseListsManagement(listsAfter, new Set());\n            const afterList = parsedAfter.find((l) => l.id === listId);\n            if (!afterList) {\n                throw new CommandExecutionError(`Could not verify list removal: list ${listId} missing from post-delete payload`);\n            }\n            const memberCountAfter = Number(afterList.members) || 0;\n            if (memberCountAfter < memberCountBefore) {\n                verifiedBy = `member_count ${memberCountBefore} → ${memberCountAfter}`;\n            } else {\n                throw new CommandExecutionError(`Failed to remove @${username} from list ${listId}: member_count unchanged (${memberCountBefore} → ${memberCountAfter}).`);\n            }\n        }\n\n    return [{","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/list-remove-core.js#L225-L261","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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"],"exampleFix":"// before\nawait listRemoveUser(page, { listId, username });\n// after\ntry {\n  await listRemoveUser(page, { listId, username });\n} catch (e) {\n  if (/Could not verify list removal: HTTP (429|5\\d\\d)/.test(e.message)) {\n    await new Promise(r => setTimeout(r, 30000));\n    await listRemoveUser(page, { listId, username });\n  } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"const cookies = await page.getCookies({ url: 'https://x.com' });\nif (!cookies.some(c => c.name === 'ct0') || !cookies.some(c => c.name === 'auth_token')) {\n  throw new Error('Log into x.com first — removal verification requires a live session.');\n}\nawait run('twitter', 'lists', { limit: 1 }); // pre-flight: API path works","typeGuard":"const isHttpErr = (v) => v && typeof v === 'object' && typeof v.__error === 'string';","tryCatchPattern":"try {\n  await listRemoveUser(page, { listId, username });\n} catch (e) {\n  const m = /Could not verify list removal: HTTP (\\d+)/.exec(e.message);\n  if (m && (m[1] === '429' || m[1].startsWith('5'))) {\n    await new Promise(r => setTimeout(r, 30000));\n    return listRemoveUser(page, { listId, username });\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["http-error","verification-failed","auth"],"backgroundTag":"http-fetch-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}