jackwener/OpenCLI · error · CommandExecutionError

List ${listId} not found among your lists.

Error message

List ${listId} not found among your lists.

What it means

The ListsManagementPageTimeline fetch succeeded, but after parsing with parseListsManagement no list in the timeline has an id equal to the requested listId. The library throws this CommandExecutionError because it can only operate on lists visible in the user's lists-management timeline.

Source

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

            return d.data?.user?.result?.rest_id || null;
        }`));
        if (!userId) throw new CommandExecutionError(`Could not resolve user @${username}`);

        // Resolve listId → name so we can match the dialog row.
        const listsQueryId = await resolveTwitterQueryId(page, 'ListsManagementPageTimeline', LISTS_MANAGEMENT_QUERY_ID);
        const listsUrl = `/i/api/graphql/${listsQueryId}/ListsManagementPageTimeline?features=${encodeURIComponent(JSON.stringify(LISTS_MANAGEMENT_FEATURES))}`;
        const listsData = 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 (listsData && listsData.__error) {
            throw new CommandExecutionError(`Could not fetch lists: ${listsData.__error}`);
        }
        const parsedLists = parseListsManagement(listsData, new Set());
        const targetList = parsedLists.find((l) => l.id === listId);
        if (!targetList) {
            throw new CommandExecutionError(`List ${listId} not found among your lists.`);
        }
        const targetName = targetList.name;

        await page.goto(`https://x.com/${username}`);
        await page.wait({ selector: '[data-testid="primaryColumn"]' });
        const uiResult = unwrapBrowserResult(await page.evaluate(`(async () => {
            const sleep = (ms) => new Promise(r => setTimeout(r, ms));
            const findOne = (sel, root = document) => root.querySelector(sel);
            const waitFor = async (fn, { timeoutMs = 8000, intervalMs = 200 } = {}) => {
                const t0 = Date.now();
                while (Date.now() - t0 < timeoutMs) { const v = fn(); if (v) return v; await sleep(intervalMs); }
                return null;
            };
            try {
                if (!window.__opencliListMutations) {
                    window.__opencliListMutations = [];
                    const origFetch = window.fetch.bind(window);
                    window.fetch = async function(...args) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the listId is the numeric ID of a list the logged-in account owns (x.com/i/lists/<id>), not a slug or someone else's list.
  2. Look up the correct numeric ID via the lists UI or ListsManagementPageTimeline and retry with it.
  3. If the account owns many lists, ensure pagination is followed or reduce list count so the target appears in the first timeline page.
  4. If parseListsManagement returns nothing for a valid response, update its parser to the current ListsManagementPageTimeline schema.
  5. Confirm the list still exists (it may have been deleted).

Example fix

// before (slug instead of numeric id)
await cli.run('twitter list-remove', { listId: 'my-cool-list', username: '@bob' });
// after
await cli.run('twitter list-remove', { listId: '1718345678901234567', username: '@bob' }); // numeric owned-list id
Defensive patterns

Strategy: validation

Validate before calling

// Ensure listId is numeric and the list is one you own before calling
if (!/^\d+$/.test(listId)) throw new Error(`listId must be numeric, got: ${listId}`);
const owned = await fetchOwnedLists(); // via x.com/i/lists UI or API
if (!owned.some(l => l.id === listId)) throw new Error(`List ${listId} is not an owned list`);

Type guard

function isOwnedList(parsedLists, listId) {
  return Array.isArray(parsedLists) && parsedLists.some(l => String(l.id) === String(listId));
}

Try / catch

try {
  await listRemoveUser(page, { listId, username });
} catch (e) {
  if (e instanceof CommandExecutionError && e.message.includes('not found among your lists')) {
    // re-enumerate owned lists and pick the correct numeric id
  } else throw e;
}

Prevention

When it happens

Trigger: Calling twitter list-remove with a listId that does not exist, belongs to another user, was deleted, or is a list the account owns but that does not appear on the first ListsManagementPageTimeline page (many lists, pagination not followed). Also triggered when parseListsManagement fails to recognize a changed timeline schema.

Common situations: Passing a list slug (e.g. 'my-list') instead of the numeric ID; using a list ID copied from a list the user follows but does not own; the list was deleted elsewhere; the account has so many lists that the target is beyond the first page; Twitter changed the timeline response shape so parseListsManagement returns an empty set.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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