{"record":{"id":"b2b5722dc3dcdf2f","repo":"jackwener/OpenCLI","slug":"twitter-lists-returned-an-unexpected-payload-shape","errorCode":null,"errorMessage":"Twitter lists returned an unexpected payload shape","messagePattern":"Twitter lists returned an unexpected payload shape","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/lists.js","lineNumber":170,"sourceCode":"        }`);\n        const queryId = unwrap(queryIdRaw) || LISTS_QUERY_ID;\n        const headers = JSON.stringify({\n            'Authorization': `Bearer ${decodeURIComponent(TWITTER_BEARER_TOKEN)}`,\n            'X-Csrf-Token': ct0,\n            'X-Twitter-Auth-Type': 'OAuth2Session',\n            'X-Twitter-Active-User': 'yes',\n        });\n        const apiUrl = buildUrl(queryId);\n        const data = await page.evaluate(`async () => {\n            const r = await fetch(${JSON.stringify(apiUrl)}, { headers: ${headers}, credentials: 'include' });\n            return r.ok ? await r.json() : { error: r.status };\n        }`);\n        if (data?.error) {\n            throw new CommandExecutionError(describeTwitterApiError('ListsManagementPageTimeline', data.error));\n        }\n        const seen = new Set();\n        if (!getListsManagementInstructions(data)) {\n            throw new CommandExecutionError('Twitter lists returned an unexpected payload shape');\n        }\n        const lists = parseListsManagement(data, seen);\n        if (lists.length === 0) {\n            throw new EmptyResultError('twitter lists', 'No owned or subscribed lists found');\n        }\n        return lists.slice(0, limit);\n    },\n});\n","sourceCodeStart":152,"sourceCodeEnd":179,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/lists.js#L152-L179","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-run the command after logging in fully; ensure the browser session reaches the lists management page, not a login/interstitial.","Update the opencli twitter CLI to a version matching the current Twitter payload shape (check for package updates/changelog).","Retry later — Twitter frequently rolls out schema variants; if persistent, file an issue with a redacted captured payload.","Inspect page.getInterceptedRequests() output to confirm the captured request is the lists-management GraphQL call and not another endpoint."],"exampleFix":"// before\nconst requests = await page.getInterceptedRequests();\nconst data = JSON.parse(requests[0].body);\n// after — pick the request that actually matches the lists GraphQL operation\nconst req = requests.find(r => r.url.includes('ListManagement') || r.url.includes('/i/api/graphql/'));\nif (!req) throw new CommandExecutionError('Lists GraphQL request not captured');\nconst data = JSON.parse(req.body);","handlingStrategy":"try-catch","validationCode":"// best-effort pre-check after capture, before parsing\nconst data = JSON.parse(req.body);\nif (!data || typeof data !== 'object' || !data?.data) {\n  throw new Error('Captured Twitter lists payload missing expected data field');\n}","typeGuard":"function isListsPayload(data) {\n  return !!data && typeof data === 'object' && (\n    'data' in data || 'instructions' in data\n  );\n}","tryCatchPattern":"try {\n  const lists = await twitterLists({ limit });\n  render(lists);\n} catch (err) {\n  if (err.message.includes('unexpected payload shape')) {\n    console.error('Twitter lists schema changed; update the CLI or retry later');\n  } else throw err;\n}","preventionTips":["Keep the opencli twitter CLI updated to track Twitter payload changes","Log captured payloads (redacted) so schema drift is diagnosable","Verify the session is fully logged in before running list commands"],"tags":["twitter","payload-shape","schema-change","browser-automation"],"backgroundTag":"unexpected-api-payload-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}