jackwener/OpenCLI · error · CommandExecutionError

Could not fetch lists: ${listsData.__error}

Error message

Could not fetch lists: ${listsData.__error}

What it means

getManagedLists fetches the user's lists via an in-page fetch of Twitter's ListsManagement endpoint. When the HTTP response is not ok, the evaluate returns { __error: 'HTTP <status>' }, which the function converts into CommandExecutionError. This means the lists could not be retrieved, so delete-time verification cannot proceed.

Source

Thrown at clis/twitter/list-delete.js:56

    longform_notetweets_inline_media_enabled: true,
    responsive_web_grok_image_annotation_enabled: true,
    responsive_web_enhance_cards_enabled: false,
};

function normalizeConfirm(value) {
    return value === true || value === 'true';
}

async function getManagedLists(page, headers) {
    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}`);
    }
    return parseListsManagement(listsData, new Set());
}

export function buildListDeleteRow({ listId, targetList }) {
    return {
        listId,
        name: targetList.name,
        members: String(targetList.members ?? '0'),
        status: 'success',
        message: `Deleted list ${targetList.name} (${targetList.members ?? '0'} members)`,
    };
}

cli({
    site: 'twitter',
    name: 'list-delete',
    access: 'write',

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-authenticate (log into x.com) and retry — HTTP 401/403 is the most common cause
  2. Check the status code in the message: 429 means slow down / wait before retrying; 404 means the endpoint/queryId changed and the code needs updating
  3. Verify the Bearer token and ct0 csrf header are current
  4. Retry after a backoff; check network/proxy access to x.com

Example fix

// before
await getManagedLists(page, headers);
// after
try {
  const lists = await getManagedLists(page, headers);
} catch (e) {
  await page.goto('https://x.com'); await page.wait(3); // refresh ct0
  const lists = await getManagedLists(page, headers);
}
Defensive patterns

Strategy: retry

Validate before calling

const cookies = await page.getCookies({ url: 'https://x.com' });
if (!cookies.some((c) => c.name === 'ct0')) throw new Error('Refresh session before fetching lists');

Type guard

function isListsPayload(d) {
  return d && typeof d === 'object' && !d.__error && (Array.isArray(d.data?.lists_by_name?.lists) || Array.isArray(d.lists));
}

Try / catch

try {
  const lists = await getManagedLists(page, headers);
} catch (e) {
  if (/Could not fetch lists: HTTP 429/.test(e.message)) await sleep(60000);
  else if (/HTTP 40[13]/.test(e.message)) await refreshLogin();
  throw e;
}

Prevention

When it happens

Trigger: The in-page fetch of listsUrl returns a non-2xx status (r.ok false) — 401/403 from expired auth, 404 from a changed GraphQL queryId/endpoint, 429 rate limit — producing listsData.__error.

Common situations: Session/csrf token expired mid-run; Twitter changed the ListsManagement queryId so the endpoint 404s; hitting rate limits from repeated list operations; network/proxy blocking x.com API calls.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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