jackwener/OpenCLI · error · CommandExecutionError

CreateList returned no list payload. Body: ${String(result.b

Error message

CreateList returned no list payload. Body: ${String(result.bodyText || '').slice(0, 300)}

What it means

This CommandExecutionError is thrown by requireCreateListResult in clis/twitter/list-create.js:60 when the CreateList response is ok JSON but lacks a data.list object and contains no errors array either. The library requires bodyJson.data.list to exist to confirm the list was created. This indicates an unexpected/empty success payload from Twitter.

Source

Thrown at clis/twitter/list-create.js:60

        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';
    if (mode !== expectedMode) {
        throw new CommandExecutionError(`CreateList returned mode ${mode}, expected ${expectedMode}.`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect the embedded Body snippet to see what Twitter actually returned in place of data.list.
  2. Check whether the list was actually created on x.com (the mutation can succeed server-side while returning an unexpected payload) before retrying to avoid duplicates.
  3. If the response shape changed, update requireCreateListResult to read the new path for the created list object.
  4. Retry after a short delay if Twitter's backend was degraded — an empty data payload can be transient.
  5. Disable browser extensions/scripts that might rewrite fetch responses inside the automated page.

Example fix

// before (old response path)
const list = result.bodyJson?.data?.list;
// after (if Twitter moved the created entity in the response)
const list = result.bodyJson?.data?.list ?? result.bodyJson?.data?.create_list;
Defensive patterns

Strategy: validation

Validate before calling

// After the call, guard the expected response shape:
function hasListPayload(result) {
  const list = result?.bodyJson?.data?.list;
  return Boolean(list && typeof list === 'object');
}

Type guard

function isCreatedListPayload(bodyJson) {
  return Boolean(
    bodyJson && typeof bodyJson === 'object' &&
    bodyJson.data && typeof bodyJson.data === 'object' &&
    bodyJson.data.list && typeof bodyJson.data.list === 'object'
  );
}

Try / catch

try {
  const row = await opencli twitter list-create name;
} catch (err) {
  if (/returned no list payload/.test(err.message)) {
    // check x.com whether the list was actually created before retrying
    const exists = await checkListExistsOnX(name);
    if (!exists) retryCreationOnce();
  } else throw err;
}

Prevention

When it happens

Trigger: Response like { data: {} }, { data: { list: null } }, or an empty JSON object with HTTP 2xx — a successful transport but no list entity returned, e.g. the mutation silently failed or Twitter changed the CreateList response shape (renamed/moved data.list).

Common situations: Twitter changing the GraphQL response schema (data.list moved or renamed), silent server-side failure returning empty data, response intercepted/modified by browser extensions or automation scripts, partial responses from a degraded Twitter backend.

Related errors


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