jackwener/OpenCLI · error · CommandExecutionError

CreateList returned malformed JSON payload. Body: ${String(r

Error message

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

What it means

This CommandExecutionError is thrown by requireCreateListResult in clis/twitter/list-create.js:52 when the CreateList response had HTTP ok status, but the body could not be parsed as JSON (or parsed to a non-object). The library validates result.bodyJson because every later check (data.list, errors) depends on a parsed object. The first 300 chars of the raw body are included to aid diagnosis.

Source

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

    if (modeRaw !== 'public' && modeRaw !== 'private') {
        throw new ArgumentError(`Invalid mode: ${JSON.stringify(kwargs.mode)}. Expected "public" or "private".`);
    }
    return { listName: name, listDescription: description, listMode: modeRaw, privateFlag: modeRaw === 'private' };
}

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)}.`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect the embedded Body snippet — if it is HTML, you are being served a web page (challenge/maintenance) instead of the API response.
  2. Re-authenticate the browser session on x.com so Twitter serves API JSON rather than a challenge page.
  3. Disable or change proxy/VPN settings that may inject or truncate response bodies.
  4. Retry after a delay if Twitter is under maintenance or degraded.
  5. If it recurs, update the automation/browser environment so fetch from page context returns the real API payload.
Defensive patterns

Strategy: validation

Validate before calling

// After the call, validate the payload shape before using it:
function hasParseableJsonResult(result) {
  return Boolean(result && result.ok && result.bodyJson && typeof result.bodyJson === 'object');
}

Type guard

function isParsedJsonObject(v) {
  return v !== null && typeof v === 'object' && !Array.isArray(v);
}

Try / catch

try {
  const row = await opencli twitter list-create name;
} catch (err) {
  if (/malformed JSON payload/.test(err.message)) {
    // body was HTML/empty — re-auth session or retry; do not parse err.message further
    await refreshXSession();
  } else throw err;
}

Prevention

When it happens

Trigger: Twitter returns HTTP 2xx but with a non-JSON body: an HTML error/interstitial page (e.g. anti-bot challenge or maintenance page served with 200), an empty body, or a truncated response captured by page.evaluate.

Common situations: x.com serving a login/challenge HTML page with 200 status instead of JSON; Twitter maintenance windows returning HTML; proxy/VPN injecting content; network interruption truncating the response so JSON.parse fails.

Understand the failure class

Related errors


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