jackwener/OpenCLI · error · CommandExecutionError
describeTwitterApiError('ListsManagementPageTimeline', data.
Error message
describeTwitterApiError('ListsManagementPageTimeline', data.error) What it means
The ListsManagementPageTimeline GraphQL call used to enumerate your lists returned an error payload ({error: status} from a non-OK fetch). The library converts it via describeTwitterApiError into a CommandExecutionError naming the GraphQL operation and status.
Source
Thrown at clis/twitter/lists.js:166
} catch {}
}
} catch {}
return null;
}`);
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
- Read the status code in the thrown message; 400 → update the library so a fresh queryId is fetched from twitter-openapi
- Re-login to x.com if the status is 401/403
- Back off and retry on 429; reduce polling frequency
- Retry later or check X API status on 5xx
Example fix
// before
const lists = await run('twitter', 'lists', { limit: 50 }); // throws on API error
// after
try {
const lists = await run('twitter', 'lists', { limit: 50 });
} catch (e) {
if (/400/.test(e.message)) console.error('queryId likely expired — update opencli/twitter-openapi.');
else if (/429/.test(e.message)) { await sleep(60000); return run('twitter', 'lists', { limit: 50 }); }
else throw e;
} 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('Log into x.com first.'); Type guard
const apiErrored = (d) => d && typeof d === 'object' && 'error' in d;
Try / catch
try {
const lists = await run('twitter', 'lists', { limit: 50 });
} catch (e) {
const m = /HTTP|\b(\d{3})\b/.exec(e.message);
if (m && m[1] === '429') { await new Promise(r => setTimeout(r, 60000)); return run('twitter', 'lists', { limit: 50 }); }
if (m && m[1] === '400') { console.error('queryId expired — update opencli/twitter-openapi.'); }
throw e;
} Prevention
- Keep the library updated — the queryId is fetched from twitter-openapi and X rotates it
- Throttle command frequency to avoid 429s
- Re-login on 401/403
- Monitor for repeated identical statuses (X-side incident or expired queryId)
When it happens
Trigger: The fetch of the lists-management API URL returned a non-OK status — 401/403 (session invalid), 400 (expired twitter-openapi queryId), 404 (endpoint removed), or 429 (rate limited) — and page.evaluate mapped it to {error: r.status}.
Common situations: Stale queryId fetched from the fa0311/twitter-openapi placeholder after X rotates GraphQL ids (HTTP 400); expired session cookies; rate limiting after repeated list commands; transient X API incidents.
Related errors
- describeTwitterApiError('ListLatestTweetsTimeline', data.err
- API_ERROR
- API_ERROR
- arXiv API HTTP ${resp.status}
- Bilibili creator comparison request failed: ${detail}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/61576a06b581fbd3.
Report an issue: GitHub.