jackwener/OpenCLI · error · CommandExecutionError

Twitter lists returned an unexpected payload shape

Error message

Twitter lists returned an unexpected payload shape

What it means

This CommandExecutionError is thrown when the Twitter lists management page data fetched via the browser session does not contain the expected instruction payload. The CLI verifies the API/page response contains recognizable lists-management data via getListsManagementInstructions(data) before parsing; if that check fails, the payload shape has drifted from what the parser understands. It indicates a UI/API contract change on Twitter/X (or an intercepted but wrong response), not a user input problem.

Source

Thrown at clis/twitter/lists.js:170

        }`);
        const queryId = unwrap(queryIdRaw) || LISTS_QUERY_ID;
        const headers = JSON.stringify({
            'Authorization': `Bearer ${decodeURIComponent(TWITTER_BEARER_TOKEN)}`,
            'X-Csrf-Token': ct0,
            'X-Twitter-Auth-Type': 'OAuth2Session',
            'X-Twitter-Active-User': 'yes',
        });
        const apiUrl = buildUrl(queryId);
        const data = await page.evaluate(`async () => {
            const r = await fetch(${JSON.stringify(apiUrl)}, { headers: ${headers}, credentials: 'include' });
            return r.ok ? await r.json() : { error: r.status };
        }`);
        if (data?.error) {
            throw new CommandExecutionError(describeTwitterApiError('ListsManagementPageTimeline', data.error));
        }
        const seen = new Set();
        if (!getListsManagementInstructions(data)) {
            throw new CommandExecutionError('Twitter lists returned an unexpected payload shape');
        }
        const lists = parseListsManagement(data, seen);
        if (lists.length === 0) {
            throw new EmptyResultError('twitter lists', 'No owned or subscribed lists found');
        }
        return lists.slice(0, limit);
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command after logging in fully; ensure the browser session reaches the lists management page, not a login/interstitial.
  2. Update the opencli twitter CLI to a version matching the current Twitter payload shape (check for package updates/changelog).
  3. Retry later — Twitter frequently rolls out schema variants; if persistent, file an issue with a redacted captured payload.
  4. Inspect page.getInterceptedRequests() output to confirm the captured request is the lists-management GraphQL call and not another endpoint.

Example fix

// before
const requests = await page.getInterceptedRequests();
const data = JSON.parse(requests[0].body);
// after — pick the request that actually matches the lists GraphQL operation
const req = requests.find(r => r.url.includes('ListManagement') || r.url.includes('/i/api/graphql/'));
if (!req) throw new CommandExecutionError('Lists GraphQL request not captured');
const data = JSON.parse(req.body);
Defensive patterns

Strategy: try-catch

Validate before calling

// best-effort pre-check after capture, before parsing
const data = JSON.parse(req.body);
if (!data || typeof data !== 'object' || !data?.data) {
  throw new Error('Captured Twitter lists payload missing expected data field');
}

Type guard

function isListsPayload(data) {
  return !!data && typeof data === 'object' && (
    'data' in data || 'instructions' in data
  );
}

Try / catch

try {
  const lists = await twitterLists({ limit });
  render(lists);
} catch (err) {
  if (err.message.includes('unexpected payload shape')) {
    console.error('Twitter lists schema changed; update the CLI or retry later');
  } else throw err;
}

Prevention

When it happens

Trigger: The 'twitter lists' command ran inside a browser session, page.waitForCapture/getInterceptedRequests returned a JSON payload, data.error was absent, but getListsManagementInstructions(data) returned falsy — i.e. the captured GraphQL response lacks the expected instructions/timeline structure that parseListsManagement expects.

Common situations: Twitter/X changed its GraphQL list-management response schema; the CLI captured the wrong network response (e.g. an embed or redirect page); a partial/logged-out render returned HTML instead of JSON; A/B test variants of the lists page ship a different payload.

Related errors


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