jackwener/OpenCLI · error · CommandExecutionError
CreateList failed: ${errors[0].message || JSON.stringify(err
Error message
CreateList failed: ${errors[0].message || JSON.stringify(errors[0])} What it means
This CommandExecutionError is thrown by requireCreateListResult in clis/twitter/list-create.js:58 when the CreateList response is valid JSON with ok status, but contains no data.list object and instead carries a GraphQL errors array. The library surfaces errors[0].message (or its JSON) verbatim. It means Twitter's GraphQL layer rejected the mutation even though the HTTP transport succeeded.
Source
Thrown at clis/twitter/list-create.js:58
function requireCreateListResult(result, expectedName, expectedMode) {
if (!result || typeof result !== 'object') {
throw new CommandExecutionError(`Unexpected result from twitter list-create: ${JSON.stringify(result)}`);
}
if (result.httpStatus === 401 || result.httpStatus === 403) {
throw new AuthRequiredError('x.com', `Twitter CreateList returned HTTP ${result.httpStatus}`);
}
if (!result.ok) {
const snippet = String(result.bodyText || '').slice(0, 300);
throw new CommandExecutionError(`HTTP ${result.httpStatus} from CreateList: ${snippet}`);
}
if (!result.bodyJson || typeof result.bodyJson !== 'object') {
throw new CommandExecutionError(`CreateList returned malformed JSON payload. Body: ${String(result.bodyText || '').slice(0, 300)}`);
}
const list = result.bodyJson?.data?.list;
if (!list || typeof list !== 'object') {
const errors = result.bodyJson?.errors;
if (Array.isArray(errors) && errors.length > 0) {
throw new CommandExecutionError(`CreateList failed: ${errors[0].message || JSON.stringify(errors[0])}`);
}
throw new CommandExecutionError(`CreateList returned no list payload. Body: ${String(result.bodyText || '').slice(0, 300)}`);
}
const id = String(list.id_str || list.id || '');
if (!/^\d+$/.test(id)) {
throw new CommandExecutionError('CreateList returned a list payload without a numeric list id.');
}
if (typeof list.name !== 'string' || !list.name.trim()) {
throw new CommandExecutionError('CreateList returned a list payload without a list name.');
}
if (list.name.trim() !== expectedName) {
throw new CommandExecutionError(`CreateList returned name ${JSON.stringify(list.name)}, expected ${JSON.stringify(expectedName)}.`);
}
const modeValue = typeof list.mode === 'string' ? list.mode : '';
if (!modeValue) {
throw new CommandExecutionError('CreateList returned a list payload without list mode.');
}
const mode = /private/i.test(modeValue) ? 'private' : 'public';View on GitHub (pinned to 49907e53dc)
Solutions
- Read the surfaced errors[0].message — it names the actual GraphQL failure (e.g. DecodeException names the offending field).
- If it is a DecodeException, update FEATURES in clis/twitter/list-create.js to exactly match the current web client's CreateList feature set (extra/unknown features are rejected).
- Ensure CREATE_LIST_QUERY_ID and FEATURES come from the same web client version — a mismatched pair triggers schema errors.
- Check the account can create lists (not suspended/restricted) and the name/description satisfy Twitter's constraints.
- Retry once before debugging deeply — Twitter sometimes returns non-fatal transient error arrays; the library itself checks for a valid list first, so a fatal throw here is a real rejection.
Example fix
// before (features drift vs current schema)
const FEATURES = { responsive_web_graphql_timeline_navigation_enabled: true, ... };
// after (capture the exact features payload from a live CreateList request in the browser devtools)
const FEATURES = { /* current exact feature set from live x.com request */ }; Defensive patterns
Strategy: validation
Validate before calling
// Pre-validate what the GraphQL layer most commonly rejects:
const name = String(kwargs.name || '').trim();
if (!name || name.length > 25) throw new Error('invalid list name');
if (String(kwargs.description || '').trim().length > 100) throw new Error('description too long');
if (!['public','private'].includes(String(kwargs.mode||'public').toLowerCase())) throw new Error('invalid mode'); Type guard
function hasGraphqlErrors(bodyJson) {
return Array.isArray(bodyJson?.errors) && bodyJson.errors.length > 0;
} Try / catch
try {
const row = await opencli twitter list-create name;
} catch (err) {
if (/^CreateList failed:/.test(err.message)) {
const reason = err.message.slice('CreateList failed:'.length).trim();
if (/DecodeException|decode/i.test(reason)) updateQueryIdAndFeatures();
else reportFatal(reason);
} else throw err;
} Prevention
- Keep CREATE_LIST_QUERY_ID and FEATURES captured from the same web client version — mismatched pairs cause DecodeException
- Do not add extra/unknown feature flags; Twitter rejects requests with an unexpected features schema
- Confirm the account is in good standing and allowed to create lists
- Remember Twitter can return non-fatal errors arrays alongside a created list — the library already checks for a valid list first
When it happens
Trigger: Response shape { errors: [ { message: ... } ] } with no data.list: e.g. schema DecodeException from a features/variables mismatch with the queryId schema, authorization/GraphQL-level denial, or duplicate/invalid list parameters rejected at the GraphQL layer.
Common situations: A newer x.com client expects different `features` than the hardcoded FEATURES map (strato DecodeException); the hardcoded queryId no longer matches the features schema; the account lacks permission to create lists (restricted/suspended); duplicate list name constraints enforced GraphQL-side.
Related errors
- ${probe.detail}
- twitter_collection_request_error
- Twitter UserByScreenName returned GraphQL errors: ${JSON.str
- Failed to add @${username} to list ${listId}: ${msg.slice(0,
- HTTP ${result.httpStatus} from CreateList: ${snippet}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/802822ce62da58c3.
Report an issue: GitHub.