jackwener/OpenCLI · error · CommandExecutionError

Malformed Twitter following user: missing screen_name

Error message

Malformed Twitter following user: missing screen_name

What it means

CommandExecutionError 'Malformed Twitter following user: missing screen_name' thrown by extractUser in clis/twitter/following.js. When a user_results.result object passes the __typename === 'User' check, its core.screen_name / legacy.screen_name are both empty, meaning X returned a user entity stripped of its handle. The library treats this as corrupt upstream data and fails loudly rather than emitting a row with an empty screen_name.

Source

Thrown at clis/twitter/following.js:74

        withBirdwatchNotes: false,
        withVoice: true,
        withV2Timeline: true,
    };
    if (cursor)
        vars.cursor = cursor;
    return `/i/api/graphql/${queryId}/Following`
        + `?variables=${encodeURIComponent(JSON.stringify(vars))}`
        + `&features=${encodeURIComponent(JSON.stringify(FEATURES))}`;
}

function extractUser(result) {
    if (!result || result.__typename !== 'User')
        return null;
    const core = result.core || {};
    const legacy = result.legacy || {};
    const screenName = core.screen_name || legacy.screen_name || '';
    if (!screenName) {
        throw new CommandExecutionError('Malformed Twitter following user: missing screen_name');
    }
    return {
        screen_name: screenName,
        name: core.name || legacy.name || '',
        bio: legacy.description || result.profile_bio?.description || '',
        followers: result.relationship_counts?.followers
            ?? legacy.followers_count
            ?? legacy.normal_followers_count
            ?? 0,
    };
}

function parseFollowing(data) {
    const users = [];
    let nextCursor = null;
    const instructions = data?.data?.user?.result?.timeline_v2?.timeline?.instructions
        || data?.data?.user?.result?.timeline?.timeline?.instructions
        || [];

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the command — ghost/degraded user entities are often transient.
  2. Refresh the x.com session (cookies) so X serves complete user payloads.
  3. If persistent, check whether X changed the User payload shape (core/legacy) and update extractUser to read the new location.
  4. Skip past the offending entry externally by lowering --limit to exclude the malformed tail rows, then report the payload shape change.

Example fix

// before
const screenName = core.screen_name || legacy.screen_name || '';
// after
const screenName = core.screen_name || legacy.screen_name || result.core?.user_legacy?.screen_name || '';  // adapt to new X payload envelope
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the payload shape you feed to extractUser before it throws
function hasScreenName(result) {
  return Boolean(result && result.__typename === 'User' &&
    ((result.core && result.core.screen_name) || (result.legacy && result.legacy.screen_name)));
}

Type guard

function isWellFormedUser(r) {
  return Boolean(r && r.__typename === 'User' &&
    typeof (r.core?.screen_name ?? r.legacy?.screen_name) === 'string' &&
    (r.core?.screen_name ?? r.legacy?.screen_name) !== '');
}

Try / catch

import { CommandExecutionError } from '@jackwener/opencli/errors';
try {
  rows = await opencli.twitter.following(user, { limit });
} catch (e) {
  if (e instanceof CommandExecutionError && /missing screen_name/.test(e.message)) {
    return await opencli.twitter.following(user, { limit }); // transient ghost entity: retry once
  }
  throw e;
}

Prevention

When it happens

Trigger: Parsing a Following GraphQL timeline where an entryId user- entry's itemContent.user_results.result has __typename 'User' but neither core.screen_name nor legacy.screen_name is populated — called from the user/zero command paths.

Common situations: X mid-flight API redesign moving screen_name to a new envelope location; suspended/ghost user entities left in a following list; partially redacted payloads served to lower-trust sessions; A/B payload variants where core is renamed (e.g. new naming while legacy is dropped).

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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